Production-grade Python library for SNOMED CT access with pluggable backends (Neo4j, Snowstorm, Snowstorm-Lite)
Project description
SNOMED CT Utilities
A Python library for working with SNOMED CT terminology, supporting Neo4j, Snowstorm, and Snowstorm-Lite backends.
This library enables powerful clinical terminology operations including concept lookups, Expression Constraint Language (ECL) queries, graph traversal, and relationship analysis. It's designed for building intelligent healthcare applications - from AI agents that need to understand medical concepts and their relationships, to clinical decision support systems, electronic health record integrations, and medical NLP pipelines. Whether you're building RAG systems that need semantic medical knowledge or traditional eHealth applications requiring standardized terminology, this library provides a unified, type-safe interface across multiple SNOMED CT backends.
Installation
pip install snomed-utils
Quick Start
Choose Your Client
The library provides three clients, each connecting to a different backend:
- Neo4JClient - Graph database backend (most features, best performance)
- SnowstormClient - Full Snowstorm terminology server (ECL native)
- SnowstormLiteClient - Lightweight FHIR-based server (basic operations)
Basic Usage
from snomed_utils import Neo4JClient, SnowstormClient, SnowstormLiteClient
# Neo4j Backend
with Neo4JClient(host="localhost", port=7687, username="neo4j", password="password") as client:
concept = client.lookup("73211009") # Diabetes mellitus
print(f"Concept: {concept.fsn}")
# ECL queries
result = client.ecl_query("<< 73211009") # All diabetes types
print(f"Found {len(result.concepts)} concepts")
# Graph operations
ancestors = client.get_ancestors("73211009")
distance = client.distance("73211009", "44054006")
# Snowstorm Backend
with SnowstormClient(url="http://localhost:8080") as client:
concept = client.lookup("73211009")
result = client.ecl_query("<< 73211009")
# Snowstorm-Lite Backend
with SnowstormLiteClient(url="http://localhost:8080") as client:
concept = client.lookup("73211009")
result = client.ecl_query("<< 73211009")
Async Support
All clients have async variants for better performance:
import asyncio
from snomed_utils import Neo4JClientAsync, SnowstormClientAsync, SnowstormLiteClientAsync
async def main():
async with Neo4JClientAsync(host="localhost", username="neo4j", password="password") as client:
await client.connect()
# Batch operations are efficient
concepts = await client.lookup_batch(["73211009", "44054006"])
print(f"Retrieved {len(concepts)} concepts")
await client.disconnect()
asyncio.run(main())
Client Reference
Neo4JClient
Configuration:
client = Neo4JClient(
host="localhost",
port=7687,
username="neo4j",
password="password",
database="neo4j"
)
Key Methods:
lookup(concept_id)- Get full concept detailslookup_batch(concept_ids)- Batch concept lookupecl_query(ecl, limit, offset)- Execute ECL queries (converted to Cypher)get_ancestors(concept_id)- Get all ancestorsget_descendants(concept_id)- Get all descendantsget_siblings(concept_id)- Get sibling conceptsdistance(source_id, target_id)- Calculate hop distancedistance_batch(pairs)- Batch distance calculationload_rf2(rf2_source, release_type, overwrite)- Load RF2 zip filerun_cypher(query, parameters)- Execute custom Cypher queries
SnowstormClient
Configuration:
client = SnowstormClient(
url="http://localhost:8080",
branch="MAIN",
admin_username="admin", # For RF2 imports
admin_password="admin"
)
Key Methods:
lookup(concept_id)- Get full concept detailslookup_batch(concept_ids)- Batch concept lookupecl_query(ecl, limit, offset)- Execute ECL queries (native)get_ancestors(concept_id)- Get all ancestors via ECLget_descendants(concept_id)- Get all descendants via ECLget_siblings(concept_id)- Get sibling conceptsimport_rf2(file_path)- Import RF2 zip fileget_normalform(concept_id)- Get normal form expression
SnowstormLiteClient
Configuration:
client = SnowstormLiteClient(
url="http://localhost:8080",
admin_username="admin", # For RF2 imports
admin_password="2212"
)
Key Methods:
lookup(concept_id)- Get basic concept detailslookup_batch(concept_ids)- Batch concept lookupecl_query(ecl, limit, offset)- Execute ECL queries via FHIR ValueSetget_ancestors(concept_id)- Limited ancestor supportget_descendants(concept_id)- Limited descendant supportimport_rf2(file_path)- Import RF2 zip fileget_normalform(concept_id)- Get normal form expression
Loading RF2 Data
Neo4j Backend
from snomed_utils import Neo4JClient
client = Neo4JClient(host="localhost", username="neo4j", password="password")
client.connect()
# Import RF2 zip file
status = client.import_rf2("/path/to/SnomedCT_InternationalRF2.zip")
print(f"Import operation ID: {status.operation_id}")
client.disconnect()
Snowstorm Backend
from snomed_utils import SnowstormClient
client = SnowstormClient(
url="http://localhost:8080",
admin_username="admin",
admin_password="admin"
)
client.connect()
# Import RF2 via admin API
status = client.import_rf2("/path/to/SnomedCT_InternationalRF2.zip")
print(f"Import operation ID: {status.operation_id}")
# Check status
import_status = client.get_import_status(status.operation_id)
print(f"Status: {import_status.status}")
client.disconnect()
Snowstorm-Lite Backend
from snomed_utils import SnowstormLiteClient
client = SnowstormLiteClient(
url="http://localhost:8080",
admin_username="admin",
admin_password="2212"
)
client.connect()
# Import RF2 via admin API
status = client.import_rf2("/path/to/SnomedCT_InternationalRF2.zip")
print(f"Import operation ID: {status.operation_id}")
client.disconnect()
Deployment Examples
Complete deployment examples with Docker Compose are available in the .github/deployment_examples/ directory.
Neo4j
cd .github/deployment_examples/neo4j
docker-compose up -d
The Neo4j browser will be available at http://localhost:7474 with:
- Username:
neo4j - Password:
12345678 - Bolt port:
7687
See: .github/deployment_examples/neo4j/docker-compose.yml
Snowstorm
Follow the official Snowstorm deployment guide:
- Repository: https://github.com/IHTSDO/snowstorm
- Docker guide: https://github.com/IHTSDO/snowstorm/blob/master/docs/using-docker.md
- Loading data: https://github.com/IHTSDO/snowstorm/blob/master/docs/loading-snomed.md
Quick start:
git clone https://github.com/IHTSDO/snowstorm.git
cd snowstorm
docker compose up -d
See: .github/deployment_examples/snowstorm/README.md
Snowstorm-Lite
docker pull snomedinternational/snowstorm-lite:latest
docker run -p 8080:8080 --name=snowstorm-lite \
-v snowstorm-lite-volume:/app/lucene-index \
snomedinternational/snowstorm-lite \
--index.path=lucene-index/data \
--admin.password=yourAdminPassword
Official documentation: https://github.com/IHTSDO/snowstorm-lite
See: .github/deployment_examples/snowstorm-lite/README.md
Data Models
All clients return consistent Pydantic models:
from snomed_utils import Concept, ECLResult, DistanceResult
# Full concept
concept = client.lookup("73211009")
print(f"ID: {concept.id}")
print(f"FSN: {concept.fsn}")
print(f"PT: {concept.pt}")
print(f"Active: {concept.active}")
print(f"Descriptions: {len(concept.descriptions)}")
print(f"Relationships: {len(concept.relationships)}")
# ECL query results
result = client.ecl_query("<< 73211009")
print(f"Total: {result.total}")
print(f"Concepts: {len(result.concepts)}")
# Distance results (Neo4j only)
distance = client.distance("73211009", "44054006")
print(f"Distance: {distance.distance}")
print(f"Path: {distance.path}")
Examples
Find All Descendants
with Neo4JClient(host="localhost", username="neo4j", password="password") as client:
# Get all types of diabetes
descendants = client.get_descendants("73211009", include_self=True)
for concept in descendants:
print(f"{concept.id}: {concept.fsn}")
ECL Query
with SnowstormClient(url="http://localhost:8080") as client:
# Find all medications containing aspirin
result = client.ecl_query(
"<< 763158003 |Medicinal product (product)| : "
"762949000 |Has precise active ingredient (attribute)| = 387458008 |Aspirin (substance)|",
limit=1000
)
print(f"Found {len(result.concepts)} aspirin products")
Batch Operations
with Neo4JClient(host="localhost", username="neo4j", password="password") as client:
# Batch lookup
concept_ids = ["73211009", "44054006", "267038008"]
concepts = client.lookup_batch(concept_ids)
# Batch distance calculation
pairs = [("73211009", "44054006"), ("267038008", "73211009")]
distances = client.distance_batch(pairs)
for dist in distances:
print(f"Distance from {dist.source_id} to {dist.target_id}: {dist.distance} hops")
License
MIT License - see LICENSE file for details.
Acknowledgments
- SNOMED International for SNOMED CT
- IHTSDO for Snowstorm and Snowstorm-Lite
- Neo4j for the graph database platform
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file snomed_utils-0.2.0.tar.gz.
File metadata
- Download URL: snomed_utils-0.2.0.tar.gz
- Upload date:
- Size: 51.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b2626c91e87ecd100fbfaee29e2aee6d3b7c59dc8c1a872a42ea858fb54756d2
|
|
| MD5 |
dbc39dc973f32cd15b3f7afe6dc61372
|
|
| BLAKE2b-256 |
d2f9bbf1b7f6cacb61c70875770c759dc81c20c8c93aa3b45a384465ea6be751
|
File details
Details for the file snomed_utils-0.2.0-py3-none-any.whl.
File metadata
- Download URL: snomed_utils-0.2.0-py3-none-any.whl
- Upload date:
- Size: 67.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
980032dfa4ba581d105b462fb49827ce321cd87796756083a824923596ada186
|
|
| MD5 |
77db04f03d092a57633d9fe10c126d64
|
|
| BLAKE2b-256 |
e7ccba8f8343945ed5d6d4619bb2da8d11aa1aeab4e579ddf9b9ccfb1c79f03e
|