Skip to main content

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 details
  • lookup_batch(concept_ids) - Batch concept lookup
  • ecl_query(ecl, limit, offset) - Execute ECL queries (converted to Cypher)
  • get_ancestors(concept_id) - Get all ancestors
  • get_descendants(concept_id) - Get all descendants
  • get_siblings(concept_id) - Get sibling concepts
  • distance(source_id, target_id) - Calculate hop distance
  • distance_batch(pairs) - Batch distance calculation
  • load_rf2(rf2_source, release_type, overwrite) - Load RF2 zip file
  • run_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 details
  • lookup_batch(concept_ids) - Batch concept lookup
  • ecl_query(ecl, limit, offset) - Execute ECL queries (native)
  • get_ancestors(concept_id) - Get all ancestors via ECL
  • get_descendants(concept_id) - Get all descendants via ECL
  • get_siblings(concept_id) - Get sibling concepts
  • import_rf2(file_path) - Import RF2 zip file
  • get_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 details
  • lookup_batch(concept_ids) - Batch concept lookup
  • ecl_query(ecl, limit, offset) - Execute ECL queries via FHIR ValueSet
  • get_ancestors(concept_id) - Limited ancestor support
  • get_descendants(concept_id) - Limited descendant support
  • import_rf2(file_path) - Import RF2 zip file
  • get_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:

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

snomed_utils-0.2.0.tar.gz (51.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

snomed_utils-0.2.0-py3-none-any.whl (67.4 kB view details)

Uploaded Python 3

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

Hashes for snomed_utils-0.2.0.tar.gz
Algorithm Hash digest
SHA256 b2626c91e87ecd100fbfaee29e2aee6d3b7c59dc8c1a872a42ea858fb54756d2
MD5 dbc39dc973f32cd15b3f7afe6dc61372
BLAKE2b-256 d2f9bbf1b7f6cacb61c70875770c759dc81c20c8c93aa3b45a384465ea6be751

See more details on using hashes here.

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

Hashes for snomed_utils-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 980032dfa4ba581d105b462fb49827ce321cd87796756083a824923596ada186
MD5 77db04f03d092a57633d9fe10c126d64
BLAKE2b-256 e7ccba8f8343945ed5d6d4619bb2da8d11aa1aeab4e579ddf9b9ccfb1c79f03e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page