11"""Provide abstraction for a VLM-to-AnyVar connection."""
22
3- import abc
4- from http import HTTPStatus
5-
63import requests
7- from anyvar .utils .types import VrsObject
4+ from anyvar .utils .types import VrsVariation
5+ from ga4gh .vrs import models
86
9- from anyvlm .anyvar .base_client import AF_ANNOTATION_TYPE , AmbiguousAnnotationError
10- from anyvlm .schemas .domain import AlleleFrequencyAnnotation
7+ from anyvlm .anyvar .base_client import BaseAnyVarClient
118
129
13- class HttpAnyVarClient (abc . ABC ):
10+ class HttpAnyVarClient (BaseAnyVarClient ):
1411 """AnyVar HTTP-based client"""
1512
1613 def __init__ (
@@ -24,9 +21,13 @@ def __init__(
2421 self .hostname = hostname
2522 self .request_timeout = request_timeout
2623
27- def put_objects (self , objects : list [VrsObject ]) -> list [VrsObject ]:
24+ def put_objects (self , objects : list [VrsVariation ]) -> list [VrsVariation ]:
2825 """Register objects with AnyVar
2926
27+ This method is intentionally naive. Future improvements could include
28+ * A better way to batch requests together to mitigate HTTP latency
29+ * A smarter dispatch for reconstructing variation model instances
30+
3031 :param objects: variation objects to register
3132 :return: completed VRS objects
3233 """
@@ -39,29 +40,18 @@ def put_objects(self, objects: list[VrsObject]) -> list[VrsObject]:
3940 timeout = self .request_timeout ,
4041 )
4142 response .raise_for_status ()
42- results .append (response .json ()["object" ])
43+ result_object = response .json ()["object" ]
44+ if result_object .get ("type" ) == "Allele" :
45+ results .append (models .Allele (** result_object ))
46+ else :
47+ raise NotImplementedError (
48+ f"Unsupported object type: { result_object .get ('type' )} "
49+ )
4350 return results
4451
45- def put_af_annotation (self , key : str , af : AlleleFrequencyAnnotation ) -> None :
46- """Add an allele frequency annotation to a variation
47-
48- :param key: VRS ID for variation being annotated
49- :param af: frequency data for for annotation
50- """
51- response = requests .post (
52- f"{ self .hostname } /variation/{ key } /annotations" ,
53- json = {
54- "annotation_type" : AF_ANNOTATION_TYPE ,
55- "annotation_value" : af .model_dump (mode = "json" , exclude_none = True ),
56- },
57- timeout = self .request_timeout ,
58- )
59- response .raise_for_status ()
60-
61- @abc .abstractmethod
6252 def search_by_interval (
6353 self , accession : str , start : int , end : int
64- ) -> list [VrsObject ]:
54+ ) -> list [VrsVariation ]:
6555 """Get all variation IDs located within the specified range
6656
6757 :param accession: sequence accession
@@ -76,31 +66,7 @@ def search_by_interval(
7666 response .raise_for_status ()
7767 return response .json ()["variations" ]
7868
79- def get_af_annotation (self , key : str ) -> AlleleFrequencyAnnotation | None :
80- """Get AF annotation for a key (object ID)
81-
82- :param key: object ID (presumably VRS ID)
83- :return: AF object if available, `None` otherwise
84- :raise KeyError: if object with given ID doesn't exist
85- """
86- response = requests .get (
87- f"{ self .hostname } /variation/{ key } /annotations/{ AF_ANNOTATION_TYPE } " ,
88- timeout = self .request_timeout ,
89- )
90- try :
91- response .raise_for_status ()
92- except requests .HTTPError as e :
93- if response .status_code == HTTPStatus .NOT_FOUND :
94- raise KeyError from e
95- raise
96- data = response .json ()
97- if len (data ["annotations" ]) == 0 :
98- return None
99- if len (data ["annotations" ]) > 1 :
100- raise AmbiguousAnnotationError
101- return AlleleFrequencyAnnotation (** data ["annotations" ][0 ]["annotation_value" ])
102-
103- def close (self ) -> None : # noqa: B027
69+ def close (self ) -> None :
10470 """Clean up AnyVar connection.
10571
10672 This is a no-op for this class.
0 commit comments