Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "mavedb"
version = "2026.2.1"
version = "2026.2.2"
description = "API for MaveDB, the database of Multiplexed Assays of Variant Effect."
license = "AGPL-3.0-only"
readme = "README.md"
Expand Down Expand Up @@ -35,7 +35,7 @@ numpy = "~1.26"
httpx = "~0.26.0"
pandas = ">=2.2.0,<3.0.0"
pydantic = "~2.10.0"
python-dotenv = "~0.20.0"
python-dotenv = "^1.2"
python-json-logger = "~2.0.7"
SQLAlchemy = "~2.0.29"
ga4gh-va-spec = "~0.4.2"
Expand All @@ -58,7 +58,7 @@ pyathena = { version = "~3.14.1", optional = true }
psycopg2 = { version = "~2.9.3", optional = true }
python-jose = { extras = ["cryptography"], version = "~3.5.0", optional = true }
python-multipart = { version = "~0.0.22", optional = true }
requests = { version = "~2.32.2", optional = true }
requests = { version = "^2.33", optional = true }
starlette = { version = "~0.49.0", optional = true }
starlette-context = { version = "^0.3.6", optional = true }
slack-sdk = { version = "~3.21.3", optional = true }
Expand Down
2 changes: 1 addition & 1 deletion src/mavedb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
logger = module_logging.getLogger(__name__)

__project__ = "mavedb-api"
__version__ = "2026.2.1"
__version__ = "2026.2.2"

logger.info(f"MaveDB {__version__}")

Expand Down
67 changes: 0 additions & 67 deletions src/mavedb/lib/clingen/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,70 +276,3 @@ def _existing_jwt(self) -> Optional[str]:

logger.debug(msg="Found existing but expired Genboree JWT.", extra=logging_context())
return None


def get_allele_registry_associations(
content_submissions: list[str], submission_response: list[Union[ClinGenAllele, ClinGenSubmissionError]]
) -> dict[str, str]:
"""
Links HGVS strings and ClinGen Canonoical Allele IDs (CAIDs) given a list of both.

Args:
content_submissions (list[str]): A list of HGVS strings to check for associations in the ClinGen Allele Registry.
submission_response (list[ClinGenAllele]): The response from the ClinGen Allele Registry submission,
which contains the registered alleles and their associated CAIDs.

Returns:
dict[str, str]: A dictionary mapping HGVS strings to their corresponding ClinGen Allele Registry IDs (CAIDs).
The keys are the HGVS strings, and the values are the CAIDs assigned by the ClinGen Allele Registry.
If no associations are found, an empty dictionary is returned.
"""
save_to_logging_context(
{
"num_hgvs_strings_to_associate": len(content_submissions),
"num_car_alleles_to_associate": len(submission_response),
}
)

if not content_submissions or not submission_response:
logger.warning(
msg="No content submissions or submission response provided for ClinGen Allele Registry association.",
extra=logging_context(),
)
return {}
else:
logger.info(
msg="Attempting to associate ClinGen Allele Registry allees with MaveDB HGVS strings.",
extra=logging_context(),
)

allele_registry_associations: dict[str, str] = {}
for registration in submission_response:
if "errorType" in registration:
logger.warning(
msg=f"Skipping errored ClinGen Allele Registry HGVS {registration.get('hgvs', 'unknown')} ({registration.get('errorType', 'unknown')}): {registration.get('message', 'unknown error message')}",
extra=logging_context(),
)
continue

# Extract the CAID from the URL (e.g., "http://reg.test.genome.network/allele/CA2513066" -> "CA2513066")
caid = registration["@id"].split("/")[-1]
alleles = (
registration.get("genomicAlleles", [])
+ registration.get("transcriptAlleles", [])
+ registration.get("aminoAcidAlleles", [])
)

for allele in alleles:
for hgvs_string in content_submissions:
if hgvs_string in allele["hgvs"]:
allele_registry_associations[hgvs_string] = caid
logger.debug(
msg=f"Found allele registry association for HGVS string '{hgvs_string}': {caid}",
extra=logging_context(),
)
break

save_to_logging_context({"num_hgvs_strings_associated_with_caid": len(allele_registry_associations)})
logger.info(msg="Done associating ClinGen Allele Registry responses.", extra=logging_context())
return allele_registry_associations
6 changes: 1 addition & 5 deletions src/mavedb/lib/types/clingen.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any, Literal, Optional, TypedDict

from typing_extensions import NotRequired, TypeGuard
from typing_extensions import NotRequired

# See: https://ldh.genome.network/docs/ldh/submit.html#content-submission-body

Expand Down Expand Up @@ -164,7 +164,3 @@ class ClinGenAlleleDefinition(TypedDict):
"position": str,
},
)


def is_car_submission_error(err: ClinGenAllele | ClinGenSubmissionError) -> TypeGuard[ClinGenSubmissionError]:
return "errorType" in err and "hgvs" in err
15 changes: 13 additions & 2 deletions src/mavedb/lib/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
import requests
import time

import requests

logger = logging.getLogger(__name__)

Expand All @@ -11,11 +11,22 @@ def request_with_backoff(
) -> requests.Response:
attempt = 0
while attempt <= backoff_limit:
logger.debug(f"Attempting request to {url}. This is attempt {attempt+1}.")
logger.debug(f"Attempting request to {url}. This is attempt {attempt + 1}.")
try:
response = requests.request(method=method, url=url, **kwargs)
response.raise_for_status()
return response
except requests.exceptions.HTTPError as exc:
status = exc.response.status_code if exc.response is not None else None
# 429 means rate-limited — retrying with backoff is the correct response.
# Other 4xx errors are client mistakes that won't improve on retry.
if status is not None and status != 429 and status < 500:
raise
logger.warning(f"Request to {url} failed with status {status}.", exc_info=exc)
backoff_time = backoff_wait * (2**attempt)
attempt += 1
logger.info(f"Retrying request to {url} in {backoff_wait} seconds.")
time.sleep(backoff_time)
except requests.exceptions.RequestException as exc:
logger.warning(f"Request to {url} failed.", exc_info=exc)
backoff_time = backoff_wait * (2**attempt)
Expand Down
Loading
Loading