"""Module for interacting with the UniProt API."""
from __future__ import annotations
import sys
import requests
from freqsap.accession import Accession
from freqsap.interfaces import ProteinVariantAPI
from freqsap.protein import Protein
from freqsap.variation import Variation
[docs]
class UniProt(ProteinVariantAPI):
"""Interface to the UniProt protein database API.
Provides methods to query protein information and variations from UniProt.
"""
def __init__(self, timeout: int = 3):
"""Initialize the UniProt API interface.
Sets up default query parameters, HTTP headers, and connection timeout.
"""
[docs]
self._params = {"fields": ["accession", "xref_dbsnp"]}
[docs]
self._timeout = timeout
[docs]
def get(self, accession: Accession) -> Protein:
"""Retrieve protein and its variations from UniProt.
Args:
accession (Accession): The protein accession identifier.
Returns:
Protein: The protein object with its variations.
"""
response = self.query(accession)
variations = [self.parse(feature) for feature in response["features"]]
return Protein(accession, variations)
[docs]
def query(self, accession: str) -> dict:
"""Query UniProt for protein data.
Args:
accession (str): The protein accession identifier.
Returns:
dict: JSON response from UniProt API.
Raises:
requests.HTTPError: If the request fails.
"""
base_url = f"https://rest.uniprot.org/uniprotkb/{accession}"
response = self.request(base_url)
if not response.ok:
response.raise_for_status()
sys.exit()
return response.json()
[docs]
def parse(self, feature: dict) -> Variation | None:
"""Parse a feature dictionary to extract variation information.
Args:
feature (dict): Feature dictionary from UniProt response.
Returns:
Variation | None: Variation object if a valid dbSNP reference is found, None otherwise.
"""
if feature["type"] != "Natural variant":
return None
xrefs = feature["featureCrossReferences"]
position = int(feature["location"]["start"]["value"])
for ref in xrefs:
if self.is_dbsnp(ref):
return Variation(ref["id"], position=position)
return None
[docs]
def request(self, url: str) -> requests.Response:
"""Make an HTTP GET request to UniProt.
Args:
url (str): The URL to request.
Returns:
requests.Response: The HTTP response.
"""
return requests.get(url, headers=self._headers, params=self._params, timeout=self._timeout)
[docs]
def is_dbsnp(self, xref: dict) -> bool:
"""Check if a cross-reference is a dbSNP reference.
Args:
xref (dict): Cross-reference dictionary.
Returns:
bool: True if the reference is from dbSNP and starts with 'rs', False otherwise.
"""
return xref.get("database") == "dbSNP" and xref.get("id", "").startswith("rs")
[docs]
def available(self) -> bool:
"""Check if the UniProt API is available.
Returns:
bool: True if the service is available, False otherwise.
"""
return self.request("https://rest.uniprot.org/uniprotkb/P68871").ok