Skip to content

Commit 299cce2

Browse files
committed
wip
1 parent a308ef2 commit 299cce2

20 files changed

+3705
-133
lines changed

src/anyvlm/anyvar/base_client.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from anyvar.utils.liftover_utils import ReferenceAssembly
77
from anyvar.utils.types import VrsVariation
8+
from ga4gh.vrs.models import Allele
89

910

1011
class AnyVarClientError(Exception):
@@ -21,6 +22,21 @@ class AnyVarClientConnectionError(AnyVarClientError):
2122
class BaseAnyVarClient(abc.ABC):
2223
"""Interface elements for an AnyVar client"""
2324

25+
@abc.abstractmethod
26+
def get_registered_allele_expression(
27+
self, expression: str, assembly: ReferenceAssembly = ReferenceAssembly.GRCH38
28+
) -> Allele | None:
29+
"""Retrieve registered VRS Allele for given allele expression
30+
31+
Currently, only expressions supported by the VRS-Python translator are supported.
32+
This could change depending on the AnyVar implementation, though, and probably
33+
can't be validated on the AnyVLM side.
34+
35+
:param expression: variation expression to get VRS Allele for
36+
:param assembly: reference assembly used in expression
37+
:return: VRS Allele if translation succeeds and VRS Allele has already been registered, else `None`
38+
"""
39+
2440
@abc.abstractmethod
2541
def put_allele_expressions(
2642
self,

src/anyvlm/anyvar/http_client.py

Lines changed: 92 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import logging
44
from collections.abc import Iterable, Sequence
5-
from http import HTTPStatus
5+
from http import HTTPMethod, HTTPStatus
66

77
import requests
88
from anyvar.utils.liftover_utils import ReferenceAssembly
@@ -33,6 +33,91 @@ def __init__(
3333
self.hostname = hostname
3434
self.request_timeout = request_timeout
3535

36+
def _make_allele_expression_request(
37+
self, expression: str, assembly: ReferenceAssembly, method: HTTPMethod
38+
) -> requests.Response | None:
39+
"""Make a request to the AnyVar variation endpoint for an allele expression
40+
41+
:param expression: variation expression to translate
42+
:param assembly: reference assembly used in expression
43+
:param method: HTTP method to use for the request. Only POST and PUT are
44+
supported.
45+
POST is used for retrieving a registered variation, while PUT is used for
46+
registering a new variation.
47+
:raises ValueError: if unsupported HTTP method is provided
48+
:raise AnyVarClientConnectionError: if connection to AnyVar service is
49+
unsuccessful
50+
:raise AnyVarClientError: if HTTP request fails for other reasons
51+
:return: HTTP response object if request is successful, else `None`
52+
"""
53+
if method not in {HTTPMethod.POST, HTTPMethod.PUT}:
54+
msg = (
55+
f"HTTP method {method} is not supported for allele expression requests"
56+
)
57+
raise ValueError(msg)
58+
59+
url = f"{self.hostname}/variation"
60+
payload = {
61+
"definition": expression,
62+
"assembly_name": assembly.value,
63+
"input_type": VrsType.ALLELE.value,
64+
}
65+
try:
66+
response = requests.request(
67+
method=method,
68+
url=url,
69+
json=payload,
70+
timeout=self.request_timeout,
71+
)
72+
except requests.ConnectionError as e:
73+
_logger.exception(
74+
"Unable to establish connection using AnyVar configured at %s",
75+
self.hostname,
76+
)
77+
raise AnyVarClientConnectionError from e
78+
79+
try:
80+
response.raise_for_status()
81+
except requests.HTTPError as e:
82+
_logger.exception(
83+
"Encountered HTTP exception submitting payload %s to %s",
84+
payload,
85+
url,
86+
)
87+
if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY:
88+
_logger.debug(
89+
"Translation failed for variant expression '%s'", expression
90+
)
91+
return None
92+
93+
if response.status_code == HTTPStatus.NOT_FOUND:
94+
_logger.debug("No variation found for expression '%s'", expression)
95+
return None
96+
97+
raise AnyVarClientError from e
98+
return response
99+
100+
def get_registered_allele_expression(
101+
self, expression: str, assembly: ReferenceAssembly = ReferenceAssembly.GRCH38
102+
) -> models.Allele | None:
103+
"""Retrieve VRS Allele for given allele expression
104+
105+
Currently, only expressions supported by the VRS-Python translator are supported.
106+
This could change depending on the AnyVar implementation, though, and probably
107+
can't be validated on the AnyVLM side.
108+
109+
:param expression: variation expression to translate
110+
:param assembly: reference assembly used in expression
111+
:return: VRS Allele if translation succeeds, else `None`
112+
"""
113+
response = self._make_allele_expression_request(
114+
expression, assembly, HTTPMethod.POST
115+
)
116+
if not response:
117+
return None
118+
119+
return models.Allele(**response.json()["data"])
120+
36121
def put_allele_expressions(
37122
self,
38123
expressions: Iterable[str],
@@ -50,41 +135,13 @@ def put_allele_expressions(
50135
else `None`, for the i'th expression
51136
:raise AnyVarClientError: for unexpected errors relating to specifics of client interface
52137
"""
53-
results = []
54-
url = f"{self.hostname}/variation"
138+
results: list[str | None] = []
55139
for expression in expressions:
56-
payload = {
57-
"definition": expression,
58-
"assembly_name": assembly.value,
59-
"input_type": VrsType.ALLELE.value,
60-
}
61-
try:
62-
response = requests.put(
63-
url,
64-
json=payload,
65-
timeout=self.request_timeout,
66-
)
67-
except requests.ConnectionError as e:
68-
_logger.exception(
69-
"Unable to establish connection using AnyVar configured at %s",
70-
self.hostname,
71-
)
72-
raise AnyVarClientConnectionError from e
73-
try:
74-
response.raise_for_status()
75-
except requests.HTTPError as e:
76-
_logger.exception(
77-
"Encountered HTTP exception submitting payload %s to %s",
78-
payload,
79-
url,
80-
)
81-
if response.status_code == HTTPStatus.UNPROCESSABLE_ENTITY:
82-
_logger.debug(
83-
"Translation failed for variant expression '%s'", expression
84-
)
85-
results.append(None)
86-
else:
87-
raise AnyVarClientError from e
140+
response = self._make_allele_expression_request(
141+
expression, assembly, HTTPMethod.PUT
142+
)
143+
if not response:
144+
results.append(None)
88145
else:
89146
results.append(response.json()["object_id"])
90147
return results

src/anyvlm/anyvar/python_client.py

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from anyvar.utils.liftover_utils import ReferenceAssembly
1010
from anyvar.utils.types import SupportedVariationType, VrsVariation
1111
from ga4gh.vrs.dataproxy import DataProxyValidationError
12+
from ga4gh.vrs.models import Allele
1213

1314
from anyvlm.anyvar.base_client import BaseAnyVarClient
1415

@@ -26,6 +27,59 @@ def __init__(self, translator: Translator, storage: Storage) -> None:
2627
"""
2728
self.av = AnyVar(translator, storage)
2829

30+
def _translate_allele_expression(
31+
self, expression: str, assembly: ReferenceAssembly = ReferenceAssembly.GRCH38
32+
) -> Allele | None:
33+
"""Translate a single allele expression to a VRS Allele ID
34+
35+
Currently, only expressions supported by the VRS-Python translator are supported.
36+
This could change depending on the AnyVar implementation, though, and probably
37+
can't be validated on the AnyVLM side.
38+
39+
:param expression: variation expression to translate
40+
:param assembly: reference assembly used in expression
41+
:return: VRS Allele if translation succeeds, else `None`
42+
"""
43+
translated_variation = None
44+
try:
45+
translated_variation = self.av.translator.translate_variation(
46+
expression,
47+
assembly=assembly.value,
48+
input_type=SupportedVariationType.ALLELE, # type: ignore
49+
)
50+
except DataProxyValidationError:
51+
_logger.exception("Found invalid base in expression %s", expression)
52+
except TranslationError:
53+
_logger.exception("Failed to translate expression: %s", expression)
54+
if isinstance(translated_variation, Allele):
55+
return translated_variation
56+
return None
57+
58+
def get_registered_allele_expression(
59+
self, expression: str, assembly: ReferenceAssembly = ReferenceAssembly.GRCH38
60+
) -> Allele | None:
61+
"""Retrieve registered VRS Allele for given allele expression
62+
63+
Currently, only expressions supported by the VRS-Python translator are supported.
64+
This could change depending on the AnyVar implementation, though, and probably
65+
can't be validated on the AnyVLM side.
66+
67+
:param expression: variation expression to get VRS Allele for
68+
:param assembly: reference assembly used in expression
69+
:return: VRS Allele if translation succeeds and VRS Allele has already been registered, else `None`
70+
"""
71+
translated_variation = self._translate_allele_expression(expression, assembly)
72+
if translated_variation and translated_variation.id:
73+
try:
74+
vrs_variation = self.av.get_object(translated_variation.id, Allele)
75+
if isinstance(vrs_variation, Allele):
76+
return vrs_variation
77+
except KeyError:
78+
_logger.exception(
79+
"VRS Allele with ID %s not found", translated_variation.id
80+
)
81+
return None
82+
2983
def put_allele_expressions(
3084
self,
3185
expressions: Iterable[str],
@@ -44,20 +98,12 @@ def put_allele_expressions(
4498
"""
4599
results = []
46100
for expression in expressions:
47-
translated_variation = None
48-
try:
49-
translated_variation = self.av.translator.translate_variation(
50-
expression,
51-
assembly=assembly.value,
52-
input_type=SupportedVariationType.ALLELE,
53-
)
54-
except DataProxyValidationError:
55-
_logger.exception("Found invalid base in expression %s", expression)
56-
except TranslationError:
57-
_logger.exception("Failed to translate expression: %s", expression)
101+
translated_variation = self._translate_allele_expression(
102+
expression, assembly
103+
)
58104
if translated_variation:
59-
self.av.put_objects([translated_variation]) # type: ignore
60-
results.append(translated_variation.id) # type: ignore
105+
self.av.put_objects([translated_variation])
106+
results.append(translated_variation.id)
61107
else:
62108
results.append(None)
63109
return results

src/anyvlm/functions/get_caf.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
"""Perform search against variant(s) contained by an AnyVar node, and construct cohort allele frequency model(s)"""
22

3-
from anyvar.utils.types import VrsVariation
43
from ga4gh.core.models import iriReference
54
from ga4gh.va_spec.base import CohortAlleleFrequencyStudyResult
6-
from ga4gh.vrs.models import Allele
75

86
from anyvlm.anyvar.base_client import BaseAnyVarClient
97
from anyvlm.storage.base_storage import Storage
10-
from anyvlm.utils.types import ChromosomeName, GrcAssemblyId, UcscAssemblyBuild
8+
from anyvlm.utils.types import (
9+
ASSEMBLY_MAP,
10+
ChromosomeName,
11+
GrcAssemblyId,
12+
NucleotideSequence,
13+
UcscAssemblyBuild,
14+
)
1115

1216

1317
def get_caf(
@@ -16,6 +20,8 @@ def get_caf(
1620
assembly_id: GrcAssemblyId | UcscAssemblyBuild,
1721
reference_name: ChromosomeName,
1822
start: int,
23+
reference_bases: NucleotideSequence,
24+
alternate_bases: NucleotideSequence,
1925
) -> list[CohortAlleleFrequencyStudyResult]:
2026
"""Retrieve Cohort Allele Frequency data for all known variants matching provided
2127
search params
@@ -29,23 +35,25 @@ def get_caf(
2935
:param start: start of range search. Uses residue coordinates (1-based)
3036
:param reference_bases: Genomic bases ('T', 'AC', etc.)
3137
:param alternate_bases: Genomic bases ('T', 'AC', etc.)
38+
:raises ValueError: if unsupported assembly ID is provided
3239
:return: list of CAFs contained in search interval
3340
"""
34-
vrs_variations: list[VrsVariation] = anyvar_client.search_by_interval(
35-
f"{assembly_id}:{reference_name}", start - 1, start
36-
)
37-
vrs_variations_map: dict[str, Allele] = {
38-
vrs_variation.id: vrs_variation
39-
for vrs_variation in vrs_variations
40-
if vrs_variation.id and isinstance(vrs_variation, Allele)
41-
}
41+
gnomad_vcf: str = f"{reference_name}-{start}-{reference_bases}-{alternate_bases}"
42+
try:
43+
assembly = ASSEMBLY_MAP[assembly_id]
44+
except KeyError as e:
45+
msg = "Unsupported assembly ID: {assembly_id}"
46+
raise ValueError(msg) from e
47+
vrs_variation = anyvar_client.get_registered_allele_expression(gnomad_vcf, assembly)
48+
if not vrs_variation:
49+
return []
4250

4351
cafs: list[CohortAlleleFrequencyStudyResult] = (
44-
anyvlm_storage.get_caf_by_vrs_allele_ids(list(vrs_variations_map))
52+
anyvlm_storage.get_caf_by_vrs_allele_id(vrs_variation.id) # type: ignore
4553
)
4654

4755
for caf in cafs:
4856
if isinstance(caf.focusAllele, iriReference):
49-
caf.focusAllele = vrs_variations_map[caf.focusAllele.root]
57+
caf.focusAllele = vrs_variation
5058

5159
return cafs

src/anyvlm/restapi/vlm.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from anyvlm.schemas.vlm import (
1414
VlmResponse,
1515
)
16+
from anyvlm.storage.base_storage import Storage
1617
from anyvlm.utils.types import (
1718
ChromosomeName,
1819
EndpointTag,
@@ -55,7 +56,14 @@ def variant_counts(
5556
],
5657
) -> VlmResponse:
5758
anyvar_client: BaseAnyVarClient = request.app.state.anyvar_client
59+
anyvlm_storage: Storage = request.app.state.anyvlm_storage
5860
caf_data: list[CohortAlleleFrequencyStudyResult] = get_caf(
59-
anyvar_client, assemblyId, referenceName, start, referenceBases, alternateBases
61+
anyvar_client,
62+
anyvlm_storage,
63+
assemblyId,
64+
referenceName,
65+
start,
66+
referenceBases,
67+
alternateBases,
6068
)
6169
return build_vlm_response_from_caf_data(caf_data)

src/anyvlm/storage/base_storage.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ def add_allele_frequencies(self, caf: CohortAlleleFrequencyStudyResult) -> None:
4141
"""
4242

4343
@abstractmethod
44-
def get_caf_by_vrs_allele_ids(
45-
self, vrs_allele_ids: list[str]
44+
def get_caf_by_vrs_allele_id(
45+
self, vrs_allele_id: str
4646
) -> list[CohortAlleleFrequencyStudyResult]:
47-
"""Retrieve cohort allele frequency study results by VRS Allele IDs
47+
"""Retrieve cohort allele frequency study results by VRS Allele ID
4848
49-
:param vrs_allele_ids: List of VRS Allele IDs
49+
:param vrs_allele_id: VRS Allele ID
5050
:return: List of cohort allele frequency study results matching given VRS
5151
Allele IDs. Will use iriReference for focusAllele
5252
"""

0 commit comments

Comments
 (0)