Skip to content

Commit

Permalink
Merge pull request #16 from TheJacksonLaboratory/G3-232-create-post-v…
Browse files Browse the repository at this point in the history
…ersions-of-the-aon-mapping-endpoints

G3 232 create post versions of the aon mapping endpoints
  • Loading branch information
bergsalex authored May 29, 2024
2 parents 8afa372 + 42814ca commit 5a2bbb2
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 16 deletions.
12 changes: 9 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@ ORTHOLOGY-ALLIANCE_COMBINED.tsv
fix.py
map.py
test.py
env.py
#env.py
.gitignore
__pycache__
flask/src/config.py
flask/alembic.ini
alembic.ini
#flask/alembic.ini
#alembic.ini
*.tsv
*.tsv.gz
.python-version
.coverage
.idea
*.pdf
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "geneweaver-aon"
version = "0.2.4"
version = "0.3.0"
description = "A service to do Homology and Orthology mapping."
authors = ["Sophie Kearney <[email protected]>", "Alexander Berger <[email protected]>"]
readme = "README.md"
Expand Down
15 changes: 8 additions & 7 deletions src/geneweaver/aon/alembic/env.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from logging.config import fileConfig
"""The script that Alembic uses to run migrations."""

from sqlalchemy import engine_from_config, text
from sqlalchemy import pool
# ruff: noqa: ERA001, ANN201

from alembic import context
from logging.config import fileConfig

from alembic import context
from sqlalchemy import engine_from_config, pool, text
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
Expand Down Expand Up @@ -64,9 +65,9 @@ def run_migrations_online():
x_arguments = context.get_x_argument(as_dictionary=True)

current_tenant = x_arguments.get("tenant")
dburi = x_arguments.get('dburi')
dburi = x_arguments.get("dburi")
if dburi:
_config_section['sqlalchemy.url'] = dburi
_config_section["sqlalchemy.url"] = dburi

connectable = engine_from_config(
_config_section,
Expand All @@ -75,7 +76,7 @@ def run_migrations_online():
)

with connectable.connect() as connection:
connection.execute(text('CREATE SCHEMA IF NOT EXISTS "%s"' % 'versions'))
connection.execute(text('CREATE SCHEMA IF NOT EXISTS "%s"' % "versions"))
connection.execute(text('CREATE SCHEMA IF NOT EXISTS "%s"' % current_tenant))
connection.execute(text('set search_path to "%s"' % current_tenant))
connection.commit()
Expand Down
31 changes: 31 additions & 0 deletions src/geneweaver/aon/controller/genes.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,37 @@ def get_gene_prefixes(db: deps.Session = Depends(deps.session)):
return genes_service.gene_prefixes(db)


@router.post("/ortholog/mapping")
def map_gene_orthologs(
db: deps.Session = Depends(deps.session),
gene_ref_ids: list[str] = None,
to_species: Optional[int] = None,
algorithm_id: Optional[int] = None,
paging_params: dict = Depends(deps.paging_parameters),
) -> list:
"""Map gene orthologs."""
return genes_service.map_gene_orthologs(
db,
gene_ref_ids,
to_species=to_species,
algorithm_id=algorithm_id,
**paging_params
)


@router.post("/homolog/mapping")
def map_gene_homologs(
db: deps.Session = Depends(deps.session),
gene_ref_ids: list[str] = None,
to_species: Optional[int] = None,
paging_params: dict = Depends(deps.paging_parameters),
) -> list:
"""Map gene orthologs."""
return genes_service.map_gene_homologs(
db, gene_ref_ids, to_species=to_species, **paging_params
)


@router.get("/{gene_id}")
def get_gene(gene_id: int, db: deps.Session = Depends(deps.session)):
"""Get gene by id."""
Expand Down
2 changes: 1 addition & 1 deletion src/geneweaver/aon/controller/orthologs.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def get_orthologs(
to_species: Optional[int] = None,
from_gene_id: Optional[int] = None,
to_gene_id: Optional[int] = None,
algorithm_id: Optional[int] = deps.DEFAULT_ALGORITHM_ID,
algorithm_id: Optional[int] = None,
best: Optional[bool] = None,
revised: Optional[bool] = None,
paging_params: dict = Depends(deps.paging_parameters),
Expand Down
7 changes: 6 additions & 1 deletion src/geneweaver/aon/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ async def lifespan(app: FastAPI) -> None:
:param app: The FastAPI application (dependency injection).
"""
logger.level = logging.getLevelName(config.LOG_LEVEL)

logger.info("Setting up DB connection pools.")
schema_versions = get_schema_versions()
app.session_managers, app.engines = set_up_sessionmanager_by_schema(schema_versions)
Expand All @@ -38,7 +40,7 @@ async def lifespan(app: FastAPI) -> None:
None if default_schema_version is None else default_schema_version.id
)
logger.info(
f"Using latest schema version as default: {default_schema_version}."
f"Using latest schema version as default: {default_schema_version.id}."
)
else:
default_version_id = next(
Expand Down Expand Up @@ -69,6 +71,8 @@ def session(request: Request) -> sessionmaker:
"""Get a session from the connection pool."""
try:
schema_version = request.state.schema_version_id
logger.info(f"Using schema version {schema_version.id}.")

try:
_session = request.app.session_managers[schema_version]()
except KeyError as e:
Expand All @@ -84,6 +88,7 @@ def session(request: Request) -> sessionmaker:
status_code=404, detail="Schema version not found."
) from e
except AttributeError:
logger.info("Using default schema version.")
_session = request.app.session()

yield _session
Expand Down
91 changes: 89 additions & 2 deletions src/geneweaver/aon/service/genes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from typing import List, Optional, Type

from geneweaver.aon.models import Gene
from geneweaver.aon.models import Gene, Homology, Ortholog, OrthologAlgorithms
from geneweaver.aon.service.utils import apply_paging
from sqlalchemy.orm import Session
from sqlalchemy.orm import Session, aliased


def get_genes(
Expand Down Expand Up @@ -62,6 +62,93 @@ def genes_by_prefix(db: Session, prefix: str) -> List[Type[Gene]]:
return db.query(Gene).filter(Gene.gn_prefix == prefix).all()


def map_gene_orthologs(
db: Session,
gene_ref_ids: List[str],
to_species: Optional[int] = None,
algorithm_id: Optional[int] = None,
start: Optional[int] = None,
limit: Optional[int] = 1000,
) -> list:
"""Map gene orthologs.
:param db: The database session.
:param gene_ref_ids: The gene reference ids to map.
:return: The mapped gene orthologs.
"""
from_gene = aliased(Gene)
to_gene = aliased(Gene)
query = (
db.query(from_gene, to_gene, Ortholog, OrthologAlgorithms)
.join(Ortholog, Ortholog.from_gene == from_gene.gn_id)
.join(to_gene, Ortholog.to_gene == to_gene.gn_id)
.join(OrthologAlgorithms, Ortholog.ort_id == OrthologAlgorithms.ort_id)
.filter(from_gene.gn_ref_id.in_(gene_ref_ids))
)

if to_species is not None:
query = query.filter(to_gene.sp_id == to_species)

if algorithm_id is not None:
query = query.filter(OrthologAlgorithms.alg_id == algorithm_id)

query = apply_paging(query, start, limit)

results = query.all()

return [
{
"from_gene": r[0].gn_ref_id,
"to_gene": r[1].gn_ref_id,
"ortholog_id": r[2].ort_id,
"algorithm_id": r[3].alg_id,
}
for r in results
]


def map_gene_homologs(
db: Session,
gene_ref_ids: List[str],
to_species: Optional[int] = None,
start: Optional[int] = None,
limit: Optional[int] = 1000,
) -> list:
"""Map gene homologs.
:param db: The database session.
:param gene_ref_ids: The gene reference ids to map.
:return: The mapped gene homologs.
"""
from_hom = aliased(Homology)
to_hom = aliased(Homology)
from_gene = aliased(Gene)
to_gene = aliased(Gene)
query = (
db.query(from_gene, from_hom, to_hom, to_gene)
.join(from_hom, from_gene.gn_id == from_hom.gn_id)
.join(to_hom, from_hom.hom_id == to_hom.hom_id)
.join(to_gene, to_hom.gn_id == to_gene.gn_id)
.filter(from_gene.gn_ref_id.in_(gene_ref_ids))
)

if to_species is not None:
query = query.filter(to_gene.sp_id == to_species)

query = apply_paging(query, start, limit)

results = query.all()

return [
{
"from_gene": r[0].gn_ref_id,
"to_gene": r[3].gn_ref_id,
"homolog_id": r[1].hom_id,
}
for r in results
]


def gene_prefixes(db: Session) -> List[str]:
"""Get all gene prefixes.
Expand Down
19 changes: 19 additions & 0 deletions src/geneweaver/aon/service/orthologs.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"""Module with functions for querying orthologs from the database."""

import logging
from typing import List, Optional, Type

from geneweaver.aon.models import Algorithm, Gene, Ortholog, OrthologAlgorithms
from geneweaver.aon.service.utils import apply_paging
from sqlalchemy.orm import Session, aliased

logger = logging.getLogger("uvicorn.error")


def get_ortholog_from_gene(db: Session, ortholog_id: int) -> Optional[Type[Gene]]:
"""Get ortholog by id.
Expand Down Expand Up @@ -59,46 +62,62 @@ def get_orthologs(
:param limit: The limit for paging.
:return: The orthologs for the provided query.
"""
logger.debug("Constructing get_orthologs query")

query = db.query(Ortholog)

if algorithm_id is not None:
logger.debug(f"Filtering get_orthologs by algorithm id: {algorithm_id}")
query = (
query.join(OrthologAlgorithms)
.join(Algorithm)
.filter(Algorithm.alg_id == algorithm_id)
)

if from_species is not None:
logger.debug(f"Filtering get_orthologs by from species: {from_species}")
from_gene = aliased(Gene)
query = query.join(from_gene, Ortholog.from_gene == from_gene.gn_id).filter(
from_gene.sp_id == from_species
)

if to_species is not None:
logger.debug(f"Filtering get_orthologs by to species: {to_species}")
to_gene = aliased(Gene)
query = query.join(to_gene, Ortholog.to_gene == to_gene.gn_id).filter(
to_gene.sp_id == to_species
)

if from_gene_id:
logger.debug(f"Filtering get_orthologs by from gene id: {from_gene_id}")
query = query.filter(Ortholog.from_gene == from_gene_id)

if to_gene_id:
logger.debug(f"Filtering get_orthologs by to gene id: {to_gene_id}")
query = query.filter(Ortholog.to_gene.id == to_gene_id)

if best is not None:
logger.debug(f"Filtering get_orthologs by best: {best}")
query = query.filter(Ortholog.ort_is_best == best)

if revised is not None:
logger.debug(f"Filtering get_orthologs by revised: {revised}")
query = query.filter(Ortholog.ort_is_best_revised == revised)

if possible_match_algorithms is not None:
logger.debug(
"Filtering get_orthologs by possible match algorithms: "
f"{possible_match_algorithms}"
)
query = query.filter(
Ortholog.ort_num_possible_match_algorithms == possible_match_algorithms
)

logger.debug(f"Using limit={limit} and start={start} for get_orthologs.")
query = apply_paging(query, start, limit)

logger.debug(f"Running get_orthologs with query: `{str(query)}`")

return query.all()


Expand Down
5 changes: 4 additions & 1 deletion src/geneweaver/aon/service/species.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
"""Species database queries."""

import logging
from typing import List, Optional, Type

from geneweaver.aon.models import GeneweaverSpecies, Species
from sqlalchemy.orm import Session

logger = logging.getLogger("uvicorn.error")


def get_species(db: Session, name: Optional[str] = None) -> List[Type[Species]]:
"""Get species.
Expand All @@ -16,7 +19,7 @@ def get_species(db: Session, name: Optional[str] = None) -> List[Type[Species]]:
base_query = db.query(Species)
if name is not None:
base_query = base_query.filter(Species.sp_name == name)
print(str(base_query))
logger.debug(f"Running get_species with query: `{str(base_query)}`")
return base_query.all()


Expand Down

0 comments on commit 5a2bbb2

Please sign in to comment.