File size: 4,711 Bytes
1c6cb4b
7b3f817
cdc75a8
a2918ce
1c6cb4b
2c00ea4
 
 
758da30
2c00ea4
1087a21
ae6d61e
a2918ce
ae6d61e
2c00ea4
1087a21
2c00ea4
1087a21
2c00ea4
 
 
 
 
 
 
a2918ce
 
 
 
 
 
 
 
 
 
 
 
a9daa10
2c00ea4
a9daa10
2c00ea4
a9daa10
2c00ea4
 
cdc75a8
a2918ce
1c6cb4b
ae6d61e
a2918ce
ae6d61e
2c00ea4
1087a21
a2918ce
 
1087a21
2c00ea4
 
758da30
ae6d61e
 
2c00ea4
758da30
ae6d61e
2c00ea4
cdc75a8
2c00ea4
 
 
 
a2918ce
 
 
 
 
 
2c00ea4
98d71a3
2c00ea4
a2918ce
2c00ea4
 
a9daa10
a2918ce
a9daa10
2c00ea4
 
758da30
2c00ea4
cdc75a8
2c00ea4
7b3f817
2c00ea4
a2918ce
2c00ea4
 
7b3f817
2c00ea4
7b3f817
2c00ea4
 
 
 
98d71a3
cdc75a8
2c00ea4
758da30
2c00ea4
 
 
 
 
 
758da30
 
2c00ea4
a2918ce
2c00ea4
 
758da30
a2918ce
758da30
2c00ea4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a2918ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# genesis/api_clients/umls_api.py
import os
import requests
from typing import List, Dict, Optional

UMLS_API_KEY = os.getenv("UMLS_API_KEY")  # Your UMLS API key from NIH UTS
UMLS_AUTH_ENDPOINT = "https://utslogin.nlm.nih.gov/cas/v1/api-key"
UMLS_BASE = "https://uts-ws.nlm.nih.gov/rest"

session = requests.Session()

# -------------------------
# Authentication Helpers
# -------------------------
def get_umls_ticket_granting_ticket() -> str:
    """
    Get the Ticket Granting Ticket (TGT) for UMLS API authentication.
    """
    if not UMLS_API_KEY:
        raise ValueError("UMLS API key not found in environment variables.")

    params = {"apikey": UMLS_API_KEY}
    r = session.post(UMLS_AUTH_ENDPOINT, data=params)
    r.raise_for_status()

    # Extract TGT location from XML
    if "location" in r.headers:
        return r.headers["location"]

    # Fallback: parse XML body
    from xml.etree import ElementTree as ET
    root = ET.fromstring(r.text)
    form = root.find(".//form")
    if form is not None and "action" in form.attrib:
        return form.attrib["action"]

    raise RuntimeError("Unable to retrieve UMLS TGT. Check API key or response format.")

def get_umls_service_ticket(tgt: str) -> str:
    """
    Use TGT to request a one-time Service Ticket (ST).
    """
    params = {"service": "http://umlsks.nlm.nih.gov"}
    r = session.post(tgt, data=params)
    r.raise_for_status()
    return r.text.strip()

# -------------------------
# Search Concepts
# -------------------------
def search_umls(term: str, page_size: int = 5) -> List[Dict]:
    """
    Search the UMLS Metathesaurus for a given term.
    Returns a list of concepts with CUI and metadata.
    """
    tgt = get_umls_ticket_granting_ticket()
    st = get_umls_service_ticket(tgt)

    params = {
        "string": term,
        "ticket": st,
        "pageSize": page_size
    }
    r = session.get(f"{UMLS_BASE}/search/current", params=params)
    r.raise_for_status()

    results = r.json().get("result", {}).get("results", [])
    concepts = []
    for res in results:
        if res.get("ui") != "NONE":
            concepts.append({
                "name": res.get("name"),
                "cui": res.get("ui"),
                "rootSource": res.get("rootSource")
            })
    return concepts

# -------------------------
# Concept Details
# -------------------------
def get_concept_details(cui: str) -> Dict:
    """
    Fetch details for a UMLS CUI (definitions, atoms, semantic types).
    """
    tgt = get_umls_ticket_granting_ticket()
    st = get_umls_service_ticket(tgt)

    r = session.get(f"{UMLS_BASE}/content/current/CUI/{cui}", params={"ticket": st})
    r.raise_for_status()
    return r.json().get("result", {})

# -------------------------
# Related Concepts
# -------------------------
def get_related_concepts(cui: str) -> List[Dict]:
    """
    Fetch related concepts for a given CUI.
    """
    tgt = get_umls_ticket_granting_ticket()
    st = get_umls_service_ticket(tgt)

    r = session.get(f"{UMLS_BASE}/content/current/CUI/{cui}/relations", params={"ticket": st})
    r.raise_for_status()

    relations = r.json().get("result", [])
    related = []
    for rel in relations:
        related.append({
            "relatedCUI": rel.get("relatedId"),
            "relationLabel": rel.get("relationLabel"),
            "relatedName": rel.get("relatedIdName")
        })
    return related

# -------------------------
# Term β†’ Semantic Network
# -------------------------
def umls_network(term: str) -> Dict:
    """
    Build a semantic network for a term: concepts, details, and related CUIs.
    """
    concepts = search_umls(term)
    network = []

    for concept in concepts:
        cui = concept["cui"]
        details = get_concept_details(cui)
        related = get_related_concepts(cui)
        network.append({
            "concept": concept,
            "details": details,
            "related": related
        })

    return {
        "term": term,
        "network": network
    }

# -------------------------
# Expansion Helper for pipeline.py
# -------------------------
def expand_with_umls(term: str, max_results: int = 5) -> List[str]:
    """
    Expand a biomedical term into related synonyms & preferred names from UMLS.
    Used by pipeline.py for ontology enrichment.
    """
    try:
        concepts = search_umls(term, page_size=max_results)
        expanded = []
        for c in concepts:
            if c["name"] and c["name"].lower() != term.lower():
                expanded.append(c["name"])
        return list(set(expanded))
    except Exception as e:
        print(f"[UMLS] Expansion failed for '{term}': {e}")
        return []