Skip to content

FHIR

Translator Registry

pluginlake.fhir.translator_registry

FHIR-to-OMOP translator registry.

Maps FHIR R4 resource types to their plugin-rosetta translators and the corresponding OMOP CDM target tables.

get_translator(resource_type)

Return a translator instance for the given FHIR resource type.

Parameters:

Name Type Description Default
resource_type str

Lowercase FHIR resource type (e.g. patient).

required

Returns:

Type Description
FhirToOmopTranslator

An instantiated translator.

Raises:

Type Description
ValueError

If the resource type is not supported.

Source code in src/pluginlake/fhir/translator_registry.py
def get_translator(resource_type: str) -> FhirToOmopTranslator:
    """Return a translator instance for the given FHIR resource type.

    Args:
        resource_type: Lowercase FHIR resource type (e.g. ``patient``).

    Returns:
        An instantiated translator.

    Raises:
        ValueError: If the resource type is not supported.
    """
    cls = _TRANSLATOR_MAP.get(resource_type)
    if cls is None:
        msg = f"Unsupported FHIR resource type: {resource_type!r}. Supported: {FHIR_RESOURCE_TYPES}"
        raise ValueError(msg)
    return cls()

Loader

pluginlake.fhir.loader

FHIR NDJSON loading functions.

load_fhir_ndjson(file_path)

Load a FHIR NDJSON file into a single-column DataFrame.

Each line is stored as a JSON string in the json_data column. Empty lines and lines that fail to parse are skipped.

Parameters:

Name Type Description Default
file_path Path

Path to the NDJSON file.

required

Returns:

Type Description
DataFrame

Polars DataFrame with a single json_data (Utf8) column.

Raises:

Type Description
FileNotFoundError

If the file does not exist.

Source code in src/pluginlake/fhir/loader.py
def load_fhir_ndjson(file_path: Path) -> pl.DataFrame:
    """Load a FHIR NDJSON file into a single-column DataFrame.

    Each line is stored as a JSON string in the ``json_data`` column.
    Empty lines and lines that fail to parse are skipped.

    Args:
        file_path: Path to the NDJSON file.

    Returns:
        Polars DataFrame with a single ``json_data`` (Utf8) column.

    Raises:
        FileNotFoundError: If the file does not exist.
    """
    if not file_path.exists():
        msg = f"FHIR NDJSON file not found: {file_path}"
        logger.error(msg)
        raise FileNotFoundError(msg)

    logger.info("Loading FHIR NDJSON: %s", file_path)

    rows: list[str] = []
    with file_path.open("rb") as f:
        for lineno, raw_line in enumerate(f, start=1):
            stripped = raw_line.strip()
            if not stripped:
                continue
            try:
                orjson.loads(stripped)
            except orjson.JSONDecodeError:
                logger.warning("Skipping invalid JSON at line %d in %s", lineno, file_path)
                continue
            rows.append(stripped.decode("utf-8"))

    logger.info("Loaded %d FHIR resources from %s", len(rows), file_path)
    return pl.DataFrame({"json_data": rows}, schema={"json_data": pl.Utf8})

load_fhir_dataset(data_dir=None, resource_types=None)

Load multiple FHIR NDJSON files from a directory.

Each file is expected to be named {resource_type}.ndjson.

Parameters:

Name Type Description Default
data_dir Path | None

Directory containing NDJSON files. Uses config default if None.

None
resource_types list[str] | None

List of resource types to load. Loads all NDJSON files if None.

None

Returns:

Type Description
dict[str, DataFrame]

Dictionary mapping resource type names to DataFrames.

Source code in src/pluginlake/fhir/loader.py
def load_fhir_dataset(
    data_dir: Path | None = None,
    resource_types: list[str] | None = None,
) -> dict[str, pl.DataFrame]:
    """Load multiple FHIR NDJSON files from a directory.

    Each file is expected to be named ``{resource_type}.ndjson``.

    Args:
        data_dir: Directory containing NDJSON files. Uses config default if None.
        resource_types: List of resource types to load. Loads all NDJSON files if None.

    Returns:
        Dictionary mapping resource type names to DataFrames.
    """
    settings = get_fhir_settings()
    data_dir = data_dir or settings.raw_data_dir

    if not data_dir.exists():
        msg = f"FHIR data directory not found: {data_dir}"
        logger.warning(msg)
        return {}

    ndjson_files = list(data_dir.glob("*.ndjson"))
    if not ndjson_files:
        logger.warning("No NDJSON files found in %s", data_dir)
        return {}

    tables: dict[str, pl.DataFrame] = {}
    for ndjson_file in ndjson_files:
        resource_type = ndjson_file.stem.lower()
        if resource_types and resource_type not in resource_types:
            continue
        try:
            tables[resource_type] = load_fhir_ndjson(ndjson_file)
        except (FileNotFoundError, OSError):
            logger.exception("Skipping %s due to error", resource_type)

    logger.info("Loaded %d FHIR resource files", len(tables))
    return tables

Configuration

pluginlake.fhir.config

FHIR module configuration.

FHIRSettings

Bases: Settings

FHIR-specific configuration.

Configuration for FHIR NDJSON ingestion and FHIR-to-OMOP translation. All paths are relative to the project root unless absolute.

Source code in src/pluginlake/fhir/config.py
class FHIRSettings(BaseSettings):
    """FHIR-specific configuration.

    Configuration for FHIR NDJSON ingestion and FHIR-to-OMOP translation.
    All paths are relative to the project root unless absolute.
    """

    model_config = SettingsConfigDict(
        env_prefix="FHIR_",
        env_file=".env",
        env_file_encoding="utf-8",
        extra="ignore",
    )

    raw_data_dir: Path = Field(
        default=Path(".data/raw/fhir"),
        description="Directory containing raw FHIR NDJSON files",
    )

    folder_watch_interval: int = Field(
        default=30,
        description="Sensor polling interval in seconds for folder-based ingestion",
    )
    folder_watch_debounce_seconds: int = Field(
        default=60,
        description="Skip files modified within this many seconds to avoid duplicate triggers",
    )

get_fhir_settings()

Get FHIR module settings.

Returns:

Type Description
FHIRSettings

Configured FHIRSettings instance.

Source code in src/pluginlake/fhir/config.py
def get_fhir_settings() -> FHIRSettings:
    """Get FHIR module settings.

    Returns:
        Configured FHIRSettings instance.
    """
    return FHIRSettings()