Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions docs/reference/utilities/fhir_helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,98 @@

The `fhir` module provides a set of helper functions to make it easier for you to work with FHIR resources.

## FHIR Version Support

HealthChain supports multiple FHIR versions: **R5** (default), **R4B**, and **STU3**. All resource creation and helper functions accept an optional `version` parameter.

### Supported Versions

| Version | Description | Package Path |
|---------|-------------|--------------|
| **R5** | FHIR Release 5 (default) | `fhir.resources.*` |
| **R4B** | FHIR R4B (Ballot) | `fhir.resources.R4B.*` |
| **STU3** | FHIR STU3 | `fhir.resources.STU3.*` |

### Basic Usage

```python
from healthchain.fhir import (
FHIRVersion,
get_fhir_resource,
set_default_version,
fhir_version_context,
convert_resource,
create_condition,
)

# Get a resource class for a specific version
Patient_R4B = get_fhir_resource("Patient", "R4B")
Patient_R5 = get_fhir_resource("Patient", FHIRVersion.R5)

# Create resources with a specific version
condition_r4b = create_condition(
subject="Patient/123",
code="38341003",
display="Hypertension",
version="R4B" # Creates R4B Condition
)

# Set the default version for the session
set_default_version("R4B")

# Use context manager for temporary version changes
with fhir_version_context("STU3"):
# All resources created here use STU3
condition = create_condition(subject="Patient/123", code="123")
```

### Version Conversion

Convert resources between FHIR versions using `convert_resource()`:

```python
from healthchain.fhir import get_fhir_resource, convert_resource

# Create an R5 Patient
Patient_R5 = get_fhir_resource("Patient")
patient_r5 = Patient_R5(id="test-123", gender="male")

# Convert to R4B
patient_r4b = convert_resource(patient_r5, "R4B")
print(patient_r4b.__class__.__module__) # fhir.resources.R4B.patient
```

!!! warning "Version Conversion Limitations"
The `convert_resource()` function uses a serialize/deserialize approach. Field mappings between FHIR versions may not be 1:1 - some fields may be added, removed, or renamed between versions. Complex resources with version-specific fields may require manual handling.

### Version Detection

Detect the FHIR version of an existing resource:

```python
from healthchain.fhir import get_resource_version, get_fhir_resource

Patient_R4B = get_fhir_resource("Patient", "R4B")
patient = Patient_R4B(id="123")

version = get_resource_version(patient)
print(version) # FHIRVersion.R4B
```

### API Reference

| Function | Description |
|----------|-------------|
| `get_fhir_resource(name, version)` | Get a resource class for a specific version |
| `get_default_version()` | Get the current default FHIR version |
| `set_default_version(version)` | Set the global default FHIR version |
| `reset_default_version()` | Reset to library default (R5) |
| `fhir_version_context(version)` | Context manager for temporary version changes |
| `convert_resource(resource, version)` | Convert a resource to a different version |
| `get_resource_version(resource)` | Detect the version of an existing resource |

---

## Resource Creation

FHIR is the modern de facto standard for storing and exchanging healthcare data, but working with [FHIR resources](https://www.hl7.org/fhir/resourcelist.html) can often involve complex and nested JSON structures with required and optional fields that vary between contexts.
Expand Down Expand Up @@ -80,6 +172,14 @@ condition = create_condition(
system="http://snomed.info/sct",
)

# Create an R4B condition
condition_r4b = create_condition(
subject="Patient/123",
code="38341003",
display="Hypertension",
version="R4B", # Optional: specify FHIR version
)

# Output the created resource
print(condition.model_dump())
```
Expand Down
4 changes: 4 additions & 0 deletions healthchain/configs/defaults.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# HealthChain Interoperability Engine Default Configuration
# This file contains default values used throughout the engine

# FHIR version configuration
fhir:
version: "R5" # Default FHIR version: R5, R4B, or STU3

defaults:
# Common defaults for all resources
common:
Expand Down
27 changes: 23 additions & 4 deletions healthchain/fhir/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
"""FHIR utilities for HealthChain."""

from healthchain.fhir.version import (
FHIRVersion,
get_fhir_resource,
get_default_version,
set_default_version,
reset_default_version,
fhir_version_context,
convert_resource,
get_resource_version,
)

from healthchain.fhir.resourcehelpers import (
create_condition,
create_medication_statement,
Expand Down Expand Up @@ -30,6 +41,7 @@
from healthchain.fhir.bundlehelpers import (
create_bundle,
add_resource,
get_resource_type,
get_resources,
set_resources,
merge_bundles,
Expand All @@ -52,6 +64,15 @@
)

__all__ = [
# Version management
"FHIRVersion",
"get_fhir_resource",
"get_default_version",
"set_default_version",
"reset_default_version",
"fhir_version_context",
"convert_resource",
"get_resource_version",
# Resource creation
"create_condition",
"create_medication_statement",
Expand All @@ -60,13 +81,10 @@
"create_patient",
"create_risk_assessment_from_prediction",
"create_document_reference",
"create_document_reference_content",
# Element creation
"create_single_codeable_concept",
"create_single_reaction",
"set_condition_category",
"read_content_attachment",
"create_document_reference",
"create_document_reference_content",
"create_single_attachment",
# Resource modification
"set_condition_category",
Expand All @@ -80,6 +98,7 @@
# Bundle operations
"create_bundle",
"add_resource",
"get_resource_type",
"get_resources",
"set_resources",
"merge_bundles",
Expand Down
28 changes: 14 additions & 14 deletions healthchain/fhir/bundlehelpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
- extract_*(): extract resources from a bundle
"""

from typing import List, Type, TypeVar, Optional, Union
from typing import List, Type, TypeVar, Optional, Union, TYPE_CHECKING
from fhir.resources.bundle import Bundle, BundleEntry
from fhir.resources.resource import Resource

if TYPE_CHECKING:
from healthchain.fhir.version import FHIRVersion

T = TypeVar("T", bound=Resource)

Expand Down Expand Up @@ -44,14 +46,19 @@ def add_resource(
bundle.entry = (bundle.entry or []) + [entry]


def get_resource_type(resource_type: Union[str, Type[Resource]]) -> Type[Resource]:
def get_resource_type(
resource_type: Union[str, Type[Resource]],
version: Optional[Union["FHIRVersion", str]] = None,
) -> Type[Resource]:
"""Get the resource type class from string or type.

Args:
resource_type: String name of the resource type (e.g. "Condition") or the type itself
version: Optional FHIR version (e.g., "R4B", "STU3", or FHIRVersion enum).
If None, uses the current default version.

Returns:
The resource type class
The resource type class for the specified version

Raises:
ValueError: If the resource type is not supported or cannot be imported
Expand All @@ -64,17 +71,10 @@ def get_resource_type(resource_type: Union[str, Type[Resource]]) -> Type[Resourc
f"Resource type must be a string or Resource class, got {type(resource_type)}"
)

try:
# Try to import the resource type dynamically from fhir.resources
module = __import__(
f"fhir.resources.{resource_type.lower()}", fromlist=[resource_type]
)
return getattr(module, resource_type)
except (ImportError, AttributeError) as e:
raise ValueError(
f"Could not import resource type: {resource_type}. "
"Make sure it is a valid FHIR resource type."
) from e
# Use version manager for dynamic import with version support
from healthchain.fhir.version import get_fhir_resource

return get_fhir_resource(resource_type, version)


def get_resources(
Expand Down
33 changes: 26 additions & 7 deletions healthchain/fhir/elementhelpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@
import base64
import datetime

from typing import Optional, List, Dict, Any
from fhir.resources.codeableconcept import CodeableConcept
from fhir.resources.codeablereference import CodeableReference
from fhir.resources.coding import Coding
from fhir.resources.attachment import Attachment
from typing import Optional, List, Dict, Any, Union, TYPE_CHECKING

if TYPE_CHECKING:
from healthchain.fhir.version import FHIRVersion

logger = logging.getLogger(__name__)

Expand All @@ -21,18 +20,25 @@ def create_single_codeable_concept(
code: str,
display: Optional[str] = None,
system: Optional[str] = "http://snomed.info/sct",
) -> CodeableConcept:
version: Optional[Union["FHIRVersion", str]] = None,
) -> Any:
"""
Create a minimal FHIR CodeableConcept with a single coding.

Args:
code: REQUIRED. The code value from the code system
display: The display name for the code
system: The code system (default: SNOMED CT)
version: FHIR version to use (e.g., "R4B", "STU3"). Defaults to current default.

Returns:
CodeableConcept: A FHIR CodeableConcept resource with a single coding
"""
from healthchain.fhir.version import get_fhir_resource

CodeableConcept = get_fhir_resource("CodeableConcept", version)
Coding = get_fhir_resource("Coding", version)

return CodeableConcept(coding=[Coding(system=system, code=code, display=display)])


Expand All @@ -41,6 +47,7 @@ def create_single_reaction(
display: Optional[str] = None,
system: Optional[str] = "http://snomed.info/sct",
severity: Optional[str] = None,
version: Optional[Union["FHIRVersion", str]] = None,
) -> List[Dict[str, Any]]:
"""Create a minimal FHIR Reaction with a single coding.

Expand All @@ -53,10 +60,17 @@ def create_single_reaction(
display: The display name for the manifestation code
system: The code system for the manifestation code (default: SNOMED CT)
severity: The severity of the reaction (mild, moderate, severe)
version: FHIR version to use (e.g., "R4B", "STU3"). Defaults to current default.

Returns:
A list containing a single FHIR Reaction dictionary with manifestation and severity fields
"""
from healthchain.fhir.version import get_fhir_resource

CodeableConcept = get_fhir_resource("CodeableConcept", version)
CodeableReference = get_fhir_resource("CodeableReference", version)
Coding = get_fhir_resource("Coding", version)

return [
{
"manifestation": [
Expand All @@ -76,7 +90,8 @@ def create_single_attachment(
data: Optional[str] = None,
url: Optional[str] = None,
title: Optional[str] = "Attachment created by HealthChain",
) -> Attachment:
version: Optional[Union["FHIRVersion", str]] = None,
) -> Any:
"""Create a minimal FHIR Attachment.

Creates a FHIR Attachment resource with basic fields. Either data or url should be provided.
Expand All @@ -87,10 +102,14 @@ def create_single_attachment(
data: The actual data content to be base64 encoded
url: The URL where the data can be found
title: A title for the attachment (default: "Attachment created by HealthChain")
version: FHIR version to use (e.g., "R4B", "STU3"). Defaults to current default.

Returns:
Attachment: A FHIR Attachment resource with basic metadata and content
"""
from healthchain.fhir.version import get_fhir_resource

Attachment = get_fhir_resource("Attachment", version)

if not data and not url:
logger.warning("No data or url provided for attachment")
Expand Down
Loading