|
""" |
|
disgenet.py Β· Disease-Gene associations helper |
|
Docs: https://www.disgenet.com/downloads (REST v1) π |
|
|
|
Change-log |
|
ββββββββββ |
|
β’ 2025-06-25 β .org β .COM redirect (301) broke calls. |
|
We now default to https://www.disgenet.com/api |
|
and still follow redirects if they add a CDN later. |
|
β’ Graceful retry + 24 h LRU-cache. |
|
β’ Empty list on any error so orchestrator never crashes. |
|
""" |
|
|
|
from __future__ import annotations |
|
import os, asyncio, httpx |
|
from functools import lru_cache |
|
from typing import List, Dict |
|
|
|
_TOKEN = os.getenv("DISGENET_KEY") |
|
_BASE = "https://www.disgenet.com/api" |
|
_HDRS = {"Accept": "application/json"} |
|
if _TOKEN: |
|
_HDRS["Authorization"] = f"Bearer {_TOKEN}" |
|
|
|
_TIMEOUT = 12 |
|
_RETRIES = 2 |
|
|
|
|
|
@lru_cache(maxsize=512) |
|
async def disease_to_genes(disease_name: str, |
|
limit: int = 10) -> List[Dict]: |
|
""" |
|
Return top-N gene associations for *disease_name*. |
|
Empty list on failure or if none found. |
|
""" |
|
url = f"{_BASE}/gda/disease/{disease_name.lower()}" |
|
params = {"source": "ALL", "format": "json"} |
|
|
|
async def _one_call() -> List[Dict]: |
|
async with httpx.AsyncClient(timeout=_TIMEOUT, |
|
headers=_HDRS, |
|
follow_redirects=True) as cli: |
|
r = await cli.get(url, params=params) |
|
if r.status_code == 404: |
|
return [] |
|
r.raise_for_status() |
|
return r.json()[:limit] |
|
|
|
delay = 0.0 |
|
for _ in range(_RETRIES): |
|
try: |
|
return await _one_call() |
|
except (httpx.HTTPStatusError, httpx.ReadTimeout): |
|
await asyncio.sleep(delay or 0.7) |
|
delay = 0.0 |
|
except Exception: |
|
break |
|
return [] |
|
|