|
1 | | -# omophub-python |
2 | | -omophub's python sdk |
| 1 | +# OMOPHub Python SDK |
| 2 | + |
| 3 | +[](https://badge.fury.io/py/omophub) |
| 4 | +[](https://pypi.org/project/omophub/) |
| 5 | +[](https://opensource.org/licenses/MIT) |
| 6 | + |
| 7 | +The official Python SDK for [OMOPHub](https://omophub.com) - a medical vocabulary API providing access to OHDSI ATHENA standardized vocabularies including SNOMED CT, ICD-10, RxNorm, LOINC, and 90+ other medical terminologies. |
| 8 | + |
| 9 | +## Installation |
| 10 | + |
| 11 | +```bash |
| 12 | +pip install omophub |
| 13 | +``` |
| 14 | + |
| 15 | +## Quick Start |
| 16 | + |
| 17 | +```python |
| 18 | +import omophub |
| 19 | + |
| 20 | +# Initialize the client |
| 21 | +client = omophub.OMOPHub(api_key="oh_xxxxxxxxx") |
| 22 | + |
| 23 | +# Get a concept by ID |
| 24 | +concept = client.concepts.get(201826) |
| 25 | +print(concept["concept_name"]) # "Type 2 diabetes mellitus" |
| 26 | + |
| 27 | +# Search for concepts |
| 28 | +results = client.search.basic("diabetes", vocabulary_ids=["SNOMED", "ICD10CM"]) |
| 29 | +for concept in results["concepts"]: |
| 30 | + print(f"{concept['concept_id']}: {concept['concept_name']}") |
| 31 | + |
| 32 | +# Get concept ancestors |
| 33 | +ancestors = client.hierarchy.ancestors(201826, max_levels=3) |
| 34 | + |
| 35 | +# Map concepts between vocabularies |
| 36 | +mappings = client.mappings.get(201826, target_vocabularies=["ICD10CM"]) |
| 37 | +``` |
| 38 | + |
| 39 | +## Async Usage |
| 40 | + |
| 41 | +```python |
| 42 | +import omophub |
| 43 | +import asyncio |
| 44 | + |
| 45 | +async def main(): |
| 46 | + async with omophub.AsyncOMOPHub(api_key="oh_xxx") as client: |
| 47 | + concept = await client.concepts.get(201826) |
| 48 | + print(concept["concept_name"]) |
| 49 | + |
| 50 | +asyncio.run(main()) |
| 51 | +``` |
| 52 | + |
| 53 | +## Configuration |
| 54 | + |
| 55 | +### API Key |
| 56 | + |
| 57 | +Set your API key in one of three ways: |
| 58 | + |
| 59 | +```python |
| 60 | +# 1. Pass directly to client |
| 61 | +client = omophub.OMOPHub(api_key="oh_xxxxxxxxx") |
| 62 | + |
| 63 | +# 2. Set environment variable |
| 64 | +# export OMOPHUB_API_KEY=oh_xxxxxxxxx |
| 65 | +client = omophub.OMOPHub() |
| 66 | + |
| 67 | +# 3. Set module-level variable |
| 68 | +import omophub |
| 69 | +omophub.api_key = "oh_xxxxxxxxx" |
| 70 | +client = omophub.OMOPHub() |
| 71 | +``` |
| 72 | + |
| 73 | +Get your API key from the [OMOPHub Dashboard](https://dashboard.omophub.com/api-keys). |
| 74 | + |
| 75 | +### Additional Options |
| 76 | + |
| 77 | +```python |
| 78 | +client = omophub.OMOPHub( |
| 79 | + api_key="oh_xxx", |
| 80 | + base_url="https://api.omophub.com/v1", # API base URL |
| 81 | + timeout=30.0, # Request timeout in seconds |
| 82 | + max_retries=3, # Retry attempts for failed requests |
| 83 | + vocab_version="2025.2", # Specific vocabulary version |
| 84 | +) |
| 85 | +``` |
| 86 | + |
| 87 | +## Resources |
| 88 | + |
| 89 | +### Concepts |
| 90 | + |
| 91 | +```python |
| 92 | +# Get concept by ID |
| 93 | +concept = client.concepts.get(201826) |
| 94 | + |
| 95 | +# Get concept by vocabulary code |
| 96 | +concept = client.concepts.get_by_code("SNOMED", "73211009") |
| 97 | + |
| 98 | +# Batch get concepts |
| 99 | +result = client.concepts.batch([201826, 4329847, 73211009]) |
| 100 | + |
| 101 | +# Get autocomplete suggestions |
| 102 | +suggestions = client.concepts.suggest("diab", vocabulary="SNOMED", limit=10) |
| 103 | + |
| 104 | +# Get related concepts |
| 105 | +related = client.concepts.related(201826, relatedness_types=["hierarchical", "semantic"]) |
| 106 | + |
| 107 | +# Get concept relationships |
| 108 | +relationships = client.concepts.relationships(201826) |
| 109 | +``` |
| 110 | + |
| 111 | +### Search |
| 112 | + |
| 113 | +```python |
| 114 | +# Basic search |
| 115 | +results = client.search.basic( |
| 116 | + "heart attack", |
| 117 | + vocabulary_ids=["SNOMED"], |
| 118 | + domain_ids=["Condition"], |
| 119 | + page=1, |
| 120 | + page_size=20, |
| 121 | +) |
| 122 | + |
| 123 | +# Advanced search with facets |
| 124 | +results = client.search.advanced( |
| 125 | + "myocardial infarction", |
| 126 | + vocabularies=["SNOMED", "ICD10CM"], |
| 127 | + standard_concepts_only=True, |
| 128 | +) |
| 129 | + |
| 130 | +# Semantic search |
| 131 | +results = client.search.semantic("chest pain with shortness of breath") |
| 132 | + |
| 133 | +# Fuzzy search (typo-tolerant) |
| 134 | +results = client.search.fuzzy("diabetis") # finds "diabetes" |
| 135 | + |
| 136 | +# Auto-pagination iterator |
| 137 | +for concept in client.search.basic_iter("diabetes", page_size=100): |
| 138 | + print(concept["concept_name"]) |
| 139 | +``` |
| 140 | + |
| 141 | +### Hierarchy |
| 142 | + |
| 143 | +```python |
| 144 | +# Get ancestors |
| 145 | +ancestors = client.hierarchy.ancestors( |
| 146 | + 201826, |
| 147 | + max_levels=5, |
| 148 | + relationship_types=["Is a"], |
| 149 | +) |
| 150 | + |
| 151 | +# Get descendants |
| 152 | +descendants = client.hierarchy.descendants( |
| 153 | + 201826, |
| 154 | + max_levels=3, |
| 155 | + standard_only=True, |
| 156 | +) |
| 157 | +``` |
| 158 | + |
| 159 | +### Mappings |
| 160 | + |
| 161 | +```python |
| 162 | +# Get mappings for a concept |
| 163 | +mappings = client.mappings.get( |
| 164 | + 201826, |
| 165 | + target_vocabularies=["ICD10CM", "Read"], |
| 166 | + include_mapping_quality=True, |
| 167 | +) |
| 168 | + |
| 169 | +# Map concepts to target vocabulary |
| 170 | +result = client.mappings.map( |
| 171 | + source_concepts=[201826, 4329847], |
| 172 | + target_vocabulary="ICD10CM", |
| 173 | +) |
| 174 | +``` |
| 175 | + |
| 176 | +### Vocabularies |
| 177 | + |
| 178 | +```python |
| 179 | +# List all vocabularies |
| 180 | +vocabularies = client.vocabularies.list(include_stats=True) |
| 181 | + |
| 182 | +# Get vocabulary details |
| 183 | +snomed = client.vocabularies.get("SNOMED", include_domains=True) |
| 184 | + |
| 185 | +# Get vocabulary statistics |
| 186 | +stats = client.vocabularies.stats("SNOMED") |
| 187 | +``` |
| 188 | + |
| 189 | +### Domains |
| 190 | + |
| 191 | +```python |
| 192 | +# List all domains |
| 193 | +domains = client.domains.list(include_statistics=True) |
| 194 | + |
| 195 | +# Get domain details |
| 196 | +condition = client.domains.get("Condition") |
| 197 | + |
| 198 | +# Get concepts in a domain |
| 199 | +concepts = client.domains.concepts("Drug", standard_only=True) |
| 200 | +``` |
| 201 | + |
| 202 | +## Error Handling |
| 203 | + |
| 204 | +```python |
| 205 | +import omophub |
| 206 | + |
| 207 | +try: |
| 208 | + client = omophub.OMOPHub(api_key="oh_xxx") |
| 209 | + concept = client.concepts.get(999999999) |
| 210 | +except omophub.NotFoundError as e: |
| 211 | + print(f"Concept not found: {e.message}") |
| 212 | +except omophub.AuthenticationError as e: |
| 213 | + print(f"Authentication failed: {e.message}") |
| 214 | +except omophub.RateLimitError as e: |
| 215 | + print(f"Rate limited. Retry after {e.retry_after} seconds") |
| 216 | +except omophub.ValidationError as e: |
| 217 | + print(f"Invalid request: {e.message}") |
| 218 | +except omophub.APIError as e: |
| 219 | + print(f"API error {e.status_code}: {e.message}") |
| 220 | +except omophub.OMOPHubError as e: |
| 221 | + print(f"SDK error: {e.message}") |
| 222 | +``` |
| 223 | + |
| 224 | +## Type Hints |
| 225 | + |
| 226 | +The SDK is fully typed with TypedDict definitions for all API responses: |
| 227 | + |
| 228 | +```python |
| 229 | +from omophub import OMOPHub, Concept |
| 230 | + |
| 231 | +client = OMOPHub(api_key="oh_xxx") |
| 232 | +concept: Concept = client.concepts.get(201826) |
| 233 | + |
| 234 | +# IDE autocomplete works for all fields |
| 235 | +print(concept["concept_id"]) |
| 236 | +print(concept["concept_name"]) |
| 237 | +print(concept["vocabulary_id"]) |
| 238 | +``` |
| 239 | + |
| 240 | +## Documentation |
| 241 | + |
| 242 | +- [Full Documentation](https://docs.omophub.com/sdks/python/overview) |
| 243 | +- [API Reference](https://docs.omophub.com/api-reference) |
| 244 | +- [Examples](https://github.com/omopHub/omophub-python/tree/main/examples) |
| 245 | + |
| 246 | +## License |
| 247 | + |
| 248 | +MIT License - see [LICENSE](LICENSE) for details. |
| 249 | + |
| 250 | +## Support |
| 251 | + |
| 252 | +- [GitHub Issues](https://github.com/omopHub/omophub-python/issues) |
| 253 | +- [Documentation](https://docs.omophub.com) |
0 commit comments