|
|
|
"""MedGenesis – Human Phenotype Ontology (HPO) async helper. |
|
|
|
Adds reliability & caching over the minimal original version. |
|
|
|
Capabilities |
|
~~~~~~~~~~~~ |
|
* `get_hpo(term_id)` – fetch full term JSON (`HP:XXXXXXX`), cached 24 h. |
|
* `search_hpo(term)` – simple keyword search, returns top hit (id + name). |
|
|
|
API docs: https://hpo.jax.org/api |
|
""" |
|
from __future__ import annotations |
|
|
|
import httpx, asyncio |
|
from functools import lru_cache |
|
from typing import Dict, Optional, List |
|
|
|
_BASE = "https://hpo.jax.org/api/hpo" |
|
_TIMEOUT = 15 |
|
_HEADERS = {"User-Agent": "MedGenesis/1.0 (https://huggingface.co/spaces)"} |
|
|
|
|
|
|
|
|
|
async def _get(url: str, *, retries: int = 3) -> Dict: |
|
delay = 2 |
|
last: Optional[httpx.Response] = None |
|
for _ in range(retries): |
|
async with httpx.AsyncClient(timeout=_TIMEOUT, headers=_HEADERS) as cli: |
|
last = await cli.get(url) |
|
if last.status_code == 200: |
|
return last.json() |
|
await asyncio.sleep(delay) |
|
delay *= 2 |
|
last.raise_for_status() |
|
|
|
|
|
|
|
|
|
@lru_cache(maxsize=512) |
|
async def get_hpo(term_id: str) -> Dict: |
|
"""Return JSON for `HP:NNNNNNN` (raises if not found).""" |
|
return await _get(f"{_BASE}/term/{term_id}") |
|
|
|
|
|
@lru_cache(maxsize=256) |
|
async def search_hpo(term: str) -> Optional[Dict]: |
|
"""Return top search hit (`id`, `name`) for free‑text query or None.""" |
|
data = await _get(f"{_BASE}/search/{term}") |
|
hits: List[Dict] = data.get("_embedded", {}).get("terms", []) |
|
return hits[0] if hits else None |
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
async def _demo(): |
|
hit = await search_hpo("microcephaly") |
|
print("Top search hit:", hit) |
|
if hit: |
|
full = await get_hpo(hit["id"]) |
|
print("Definition:", full.get("definition")) |
|
asyncio.run(_demo()) |
|
|