Skip to content

FHIR Model Factory

Main class and utilities for dynamically constructing FHIR resource models.

T module-attribute

T = TypeVar('T', bound=BaseModel)

get_type_choice_value_by_base

get_type_choice_value_by_base(instance: BaseModel, base: str) -> Any

Retrieve the value of a type-choice field in an instance based on the field name starting with a specific base string.

Parameters:

Name Type Description Default
instance object

The instance object to retrieve the value from.

required
base str

The base string that the field name should start with.

required

Returns:

Name Type Description
value Any

The value of the first field found in the instance that starts with the specified base string, or None if no such field exists or the value is None.

Source code in fhircraft/fhir/resources/validators.py
def get_type_choice_value_by_base(instance: BaseModel, base: str) -> Any:
    """
    Retrieve the value of a type-choice field in an instance based on the field
    name starting with a specific base string.

    Args:
        instance (object): The instance object to retrieve the value from.
        base (str): The base string that the field name should start with.

    Returns:
        value (Any): The value of the first field found in the instance that starts with the specified base string,
                    or `None` if no such field exists or the value is `None`.
    """
    for field in instance.__class__.model_fields:
        if field.startswith(base):
            value = getattr(instance, field)
            if value is not None:
                return value

validate_FHIR_element_fixed_value

validate_FHIR_element_fixed_value(cls: Any, element: Union[FHIRBaseModel, List[FHIRBaseModel], Any], constant: Union[FHIRBaseModel, List[FHIRBaseModel], Any]) -> Any

Validate the FHIR element against a specified constant value and return the element if it fulfills the constant.

Parameters:

Name Type Description Default
cls Any

Placeholder for an argument that is not used in the function.

required
element Union[FHIRBaseModel, List[FHIRBaseModel]]

The FHIR element to validate against the constant.

required
constant Union[FHIRBaseModel, List[FHIRBaseModel]]

The constant value to validate the element against.

required

Returns:

Type Description
Any

Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR element.

Raises:

Type Description
AssertionError

If the element does not fulfill the specified constant.

Source code in fhircraft/fhir/resources/validators.py
def validate_FHIR_element_fixed_value(
    cls: Any,
    element: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
    constant: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
) -> Any:
    """
    Validate the FHIR element against a specified constant value and return the element if it fulfills the constant.

    Args:
        cls (Any): Placeholder for an argument that is not used in the function.
        element (Union[FHIRBaseModel, List[FHIRBaseModel]]): The FHIR element to validate against the constant.
        constant (Union[FHIRBaseModel, List[FHIRBaseModel]]): The constant value to validate the element against.

    Returns:
        Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR element.

    Raises:
        AssertionError: If the element does not fulfill the specified constant.
    """
    from fhircraft.fhir.resources.base import FHIRBaseModel

    config = get_config()
    if config.mode == "skip":
        return element

    if isinstance(constant, list):
        constant = constant[0]
    _element = element[0] if isinstance(element, list) else element
    try:
        if isinstance(constant, FHIRBaseModel) and isinstance(_element, FHIRBaseModel):
            assert constant.model_dump() == _element.model_dump()
        else:
            assert constant == _element
    except AssertionError as e:
        error = f"Value does not fulfill constant:\n{constant.model_dump_json(indent=2) if isinstance(constant, FHIRBaseModel) else constant}"
        if config.mode == "lenient":
            warnings.warn(error)
        else:
            raise AssertionError(error)
    return element

validate_FHIR_element_pattern

validate_FHIR_element_pattern(cls: Any, element: Union[FHIRBaseModel, List[FHIRBaseModel], Any], pattern: Union[FHIRBaseModel, List[FHIRBaseModel], Any]) -> Any

Validate the FHIR element against a specified pattern and return the element if it fulfills the pattern.

Parameters:

Name Type Description Default
cls Any

Placeholder for an argument that is not used in the function.

required
element Union[FHIRBaseModel, List[FHIRBaseModel]]

The FHIR element to validate against the pattern.

required
pattern Union[FHIRBaseModel, List[FHIRBaseModel]]

The pattern to validate the element against.

required

Returns:

Type Description
Any

Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR element.

Raises:

Type Description
AssertionError

If the element does not fulfill the specified pattern.

Source code in fhircraft/fhir/resources/validators.py
def validate_FHIR_element_pattern(
    cls: Any,
    element: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
    pattern: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
) -> Any:
    """
    Validate the FHIR element against a specified pattern and return the element if it fulfills the pattern.

    Args:
        cls (Any): Placeholder for an argument that is not used in the function.
        element (Union[FHIRBaseModel, List[FHIRBaseModel]]): The FHIR element to validate against the pattern.
        pattern (Union[FHIRBaseModel, List[FHIRBaseModel]]): The pattern to validate the element against.

    Returns:
        Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR element.

    Raises:
        AssertionError: If the element does not fulfill the specified pattern.
    """
    from fhircraft.fhir.resources.base import FHIRBaseModel

    config = get_config()
    if config.mode == "skip":
        return element

    if isinstance(pattern, list):
        pattern = pattern[0]
    _element = element[0] if isinstance(element, list) else element
    _element = (
        _element.model_dump() if isinstance(_element, FHIRBaseModel) else _element
    )
    _pattern = pattern.model_dump() if isinstance(pattern, FHIRBaseModel) else pattern
    try:
        if isinstance(_pattern, dict):
            assert is_dict_subset(_pattern, _element)
        else:
            assert _element == _pattern
    except AssertionError:
        error = f"Value does not fulfill pattern:\n{pattern.model_dump_json(indent=2) if isinstance(pattern, FHIRBaseModel) else pattern}"
        if config.mode == "lenient":
            warnings.warn(str(error))
        else:
            raise AssertionError(str(error))
    return element

validate_FHIR_model_fixed_value

validate_FHIR_model_fixed_value(model: Union[FHIRBaseModel, List[FHIRBaseModel], Any], constant: Union[FHIRBaseModel, List[FHIRBaseModel], Any]) -> Any

Validate the FHIR model against a specified constant value and return the model if it fulfills the constant.

Parameters:

Name Type Description Default
model Union[FHIRBaseModel, List[FHIRBaseModel]]

The FHIR model to validate against the constant.

required
constant Union[FHIRBaseModel, List[FHIRBaseModel]]

The constant value to validate the model against.

required

Returns:

Type Description
Any

Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR element.

Raises:

Type Description
AssertionError

If the element does not fulfill the specified constant.

Source code in fhircraft/fhir/resources/validators.py
def validate_FHIR_model_fixed_value(
    model: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
    constant: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
) -> Any:
    """
    Validate the FHIR model against a specified constant value and return the model if it fulfills the constant.

    Args:
        model (Union[FHIRBaseModel, List[FHIRBaseModel]]): The FHIR model to validate against the constant.
        constant (Union[FHIRBaseModel, List[FHIRBaseModel]]): The constant value to validate the model against.

    Returns:
        Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR element.

    Raises:
        AssertionError: If the element does not fulfill the specified constant.
    """
    return validate_FHIR_element_fixed_value(cls=None, element=model, constant=constant)

validate_FHIR_model_pattern

validate_FHIR_model_pattern(model: Union[FHIRBaseModel, List[FHIRBaseModel], Any], pattern: Union[FHIRBaseModel, List[FHIRBaseModel], Any]) -> Any

Validate the FHIR model against a specified pattern and return the model if it fulfills the pattern.

Parameters:

Name Type Description Default
model Union[FHIRBaseModel, List[FHIRBaseModel]]

The FHIR model to validate against the pattern.

required
pattern Union[FHIRBaseModel, List[FHIRBaseModel]]

The pattern to validate the model against.

required

Returns:

Type Description
Any

Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR model.

Raises:

Type Description
AssertionError

If the model does not fulfill the specified pattern.

Source code in fhircraft/fhir/resources/validators.py
def validate_FHIR_model_pattern(
    model: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
    pattern: Union["FHIRBaseModel", List["FHIRBaseModel"], Any],
) -> Any:
    """
    Validate the FHIR model against a specified pattern and return the model if it fulfills the pattern.

    Args:
        model (Union[FHIRBaseModel, List[FHIRBaseModel]]): The FHIR model to validate against the pattern.
        pattern (Union[FHIRBaseModel, List[FHIRBaseModel]]): The pattern to validate the model against.

    Returns:
        Union[FHIRBaseModel, List[FHIRBaseModel]]: The validated FHIR model.

    Raises:
        AssertionError: If the model does not fulfill the specified pattern.
    """
    return validate_FHIR_element_pattern(cls=None, element=model, pattern=pattern)

validate_element_constraint

validate_element_constraint(instance: T, elements: Sequence[str], expression: str, human: str, key: str, severity: str) -> T

Validates a FHIR element constraint based on a FHIRPath expression.

Parameters:

Name Type Description Default
instance T

The instance to be validated.

required
elements Sequence[str]

The elements to be validated.

required
expression str

The FHIRPath expression to evaluate.

required
human str

A human-readable description of the constraint.

required
key str

The key associated with the constraint.

required
severity str

The severity level of the constraint ('warning' or 'error').

required

Returns:

Name Type Description
Any T

The validated value.

Raises:

Type Description
AssertionError

If the validation fails and severity is not warning.

Warning

If the validation fails and severity is warning.

Source code in fhircraft/fhir/resources/validators.py
def validate_element_constraint(
    instance: T,
    elements: Sequence[str],
    expression: str,
    human: str,
    key: str,
    severity: str,
) -> T:
    """
    Validates a FHIR element constraint based on a FHIRPath expression.

    Args:
        instance (T): The instance to be validated.
        elements (Sequence[str]): The elements to be validated.
        expression (str): The FHIRPath expression to evaluate.
        human (str): A human-readable description of the constraint.
        key (str): The key associated with the constraint.
        severity (str): The severity level of the constraint ('warning' or 'error').

    Returns:
        Any: The validated value.

    Raises:
        AssertionError: If the validation fails and severity is not `warning`.
        Warning: If the validation fails and severity is `warning`.
    """

    config = get_config()
    if config.mode == "skip":
        return instance

    values = {}

    def _get_path_value(obj: Any, element_path: str, current_path: str = "") -> None:
        """
        Recursively extract values from nested object paths, handling lists correctly.

        Args:
            obj: Current object to traverse
            element_path: Remaining path to traverse (dot-separated)
            current_path: Path traversed so far (for error reporting)
        """
        if not element_path:
            # We've reached the end of the path
            values[current_path] = obj
            return

        parts = element_path.split(".", 1)
        current_attr = parts[0]
        remaining_path = parts[1] if len(parts) > 1 else ""

        # Build the new current path
        new_current_path = (
            f"{current_path}.{current_attr}" if current_path else current_attr
        )

        # Get the attribute value
        current_value = getattr(obj, current_attr, None) if obj is not None else None

        if current_value is None:
            # Attribute doesn't exist or is None
            values[
                (
                    new_current_path
                    if not remaining_path
                    else f"{new_current_path}.{remaining_path}"
                )
            ] = None
            return

        if isinstance(current_value, list):
            if not remaining_path:
                # We want the list itself
                values[new_current_path] = current_value
            else:
                # We need to traverse into each item in the list
                for idx, item in enumerate(current_value):
                    item_path = f"{new_current_path}[{idx}]"
                    _get_path_value(item, remaining_path, item_path)
        else:
            if not remaining_path:
                # We want this value
                values[new_current_path] = current_value
            else:
                # Continue traversing
                _get_path_value(current_value, remaining_path, new_current_path)

    for element_path in elements:
        _get_path_value(instance, element_path)

    for path, value in values.items():
        _validate_FHIR_element_constraint(
            value, instance, expression, human, key, severity, element=path
        )
    return instance

validate_model_constraint

validate_model_constraint(instance: T, expression: str, human: str, key: str, severity: str) -> T

Validates a FHIR model constraint based on a FHIRPath expression.

Parameters:

Name Type Description Default
instance T

Instance of the model to be validated.

required
expression str

The FHIRPath expression to evaluate.

required
human str

A human-readable description of the constraint.

required
key str

The key associated with the constraint.

required
severity str

The severity level of the constraint ('warning' or 'error').

required

Returns:

Name Type Description
instance type[T]

The validated model instance.

Raises:

Type Description
AssertionError

If the validation fails and severity is not warning.

Warning

If the validation fails and severity is warning.

Source code in fhircraft/fhir/resources/validators.py
def validate_model_constraint(
    instance: T, expression: str, human: str, key: str, severity: str
) -> T:
    """
    Validates a FHIR model constraint based on a FHIRPath expression.

    Args:
        instance (T): Instance of the model to be validated.
        expression (str): The FHIRPath expression to evaluate.
        human (str): A human-readable description of the constraint.
        key (str): The key associated with the constraint.
        severity (str): The severity level of the constraint ('warning' or 'error').

    Returns:
        instance (type[T]): The validated model instance.

    Raises:
        AssertionError: If the validation fails and severity is not `warning`.
        Warning: If the validation fails and severity is `warning`.
    """
    config = get_config()
    if config.mode == "skip":
        return instance
    return _validate_FHIR_element_constraint(
        instance, instance, expression, human, key, severity
    )

validate_slicing_cardinalities

validate_slicing_cardinalities(cls: Any, values: List[Any] | None, field_name: str) -> List[FHIRSliceModel] | None

Validates the cardinalities of FHIR slices for a specific field within a FHIR resource.

Parameters:

Name Type Description Default
cls Any

The Pydantic FHIR model class.

required
values List[Any]

List of values for the field.

required
field_name str

The name of the field to validate.

required

Returns:

Type Description
List[FHIRSliceModel] | None

List[FHIRSliceModel]: The validated list of values.

Raises:

Type Description
AssertionError

If cardinality constraints are violated for any slice.

Source code in fhircraft/fhir/resources/validators.py
def validate_slicing_cardinalities(
    cls: Any, values: List[Any] | None, field_name: str
) -> List["FHIRSliceModel"] | None:
    """
    Validates the cardinalities of FHIR slices for a specific field within a FHIR resource.

    Args:
        cls (Any): The Pydantic FHIR model class.
        values (List[Any]): List of values for the field.
        field_name (str): The name of the field to validate.

    Returns:
        List[FHIRSliceModel]: The validated list of values.

    Raises:
        AssertionError: If cardinality constraints are violated for any slice.
    """
    from fhircraft.config import get_config
    from fhircraft.fhir.resources.base import FHIRSliceModel

    config = get_config()
    if config.mode == "skip":
        return values

    if values is None:
        return values
    slices = get_all_models_from_field(
        cls.model_fields[field_name], issubclass_of=FHIRSliceModel
    )
    for slice in slices:
        slice_instances_count = sum([isinstance(value, slice) for value in values])
        # Only validate cardinalities if there are instances of the slice present
        if slice_instances_count > 0:
            try:
                assert (
                    slice_instances_count >= slice.min_cardinality
                ), f"Slice '{slice.__name__}' for field '{field_name}' violates its min. cardinality. \
                        Requires min. cardinality of {slice.min_cardinality}, but got {slice_instances_count}"
                if slice.max_cardinality is not None:
                    assert (
                        slice_instances_count <= slice.max_cardinality
                    ), f"Slice '{slice.__name__}' for field '{field_name}' violates its max. cardinality. \
                            Requires max. cardinality of {slice.max_cardinality}, but got {slice_instances_count}"
            except AssertionError as e:
                if config.mode == "lenient":
                    warnings.warn(str(e))
                else:
                    raise
    return values

validate_type_choice_element

validate_type_choice_element(instance: T, field_types: List[Any], field_name_base: str, required: bool = False, non_allowed_types=[]) -> T

Validate the type choice element for a given instance.

Parameters:

Name Type Description Default
instance T

The instance to validate.

required
field_types List[Any]

List of field types to check.

required
field_name_base str

Base name of the field.

required
required bool

Whether the type choice element is required.

False
non_allowed_types List[Any] | None

List of types that are not allowed for this element (for negative checks).

[]

Returns:

Name Type Description
T T

The validated instance.

Raises:

Type Description
AssertionError

If more than one value is set for the type choice element or if a non-allowed type is set.

Source code in fhircraft/fhir/resources/validators.py
def validate_type_choice_element(
    instance: T,
    field_types: List[Any],
    field_name_base: str,
    required: bool = False,
    non_allowed_types=[],
) -> T:
    """
    Validate the type choice element for a given instance.

    Args:
        instance (T): The instance to validate.
        field_types (List[Any]): List of field types to check.
        field_name_base (str): Base name of the field.
        required (bool): Whether the type choice element is required.
        non_allowed_types (List[Any] | None): List of types that are not allowed for this element (for negative checks).

    Returns:
        T: The validated instance.

    Raises:
        AssertionError: If more than one value is set for the type choice element or if a non-allowed type is set.
    """

    config = get_config()
    if config.mode == "skip":
        return instance

    _field_types: List[str] = [
        (
            capitalize(field_type)
            if isinstance(field_type, str)
            else capitalize(str(field_type.__name__))
        )
        for field_type in field_types
    ]
    types_set_count = sum(
        (
            element is not None
            if not isinstance(
                element := getattr(
                    instance,
                    (field_name_base + field_type),
                    None,
                ),
                FHIRPrimitiveModel,
            )
            else element.value is not None
        )
        for field_type in _field_types
    )

    def _assert(condition: bool, message: str) -> None:
        if not condition:
            if config.mode == "lenient":
                warnings.warn(message)
            else:
                raise AssertionError(message)

    _assert(
        types_set_count <= 1,
        f"Type choice element {field_name_base}[x] can only have one value set.",
    )
    _assert(
        not required or types_set_count > 0,
        f"Type choice element {field_name_base}[x] must have one value set. Got {types_set_count}.",
    )
    all_types = [
        field.replace(field_name_base, "")
        for field in instance.__class__.model_fields
        if field.startswith(field_name_base)
    ]
    # Check that non-allowed types are not set
    non_allowed_types = non_allowed_types or [
        field_type for field_type in all_types if field_type not in _field_types
    ]
    if non_allowed_types:
        for non_allowed_type in non_allowed_types:
            field_name = field_name_base + (
                non_allowed_type
                if isinstance(non_allowed_type, str)
                else non_allowed_type.__name__
            )
            value = getattr(instance, field_name, None)
            _assert(
                value is None,
                f"Type choice element {field_name_base}[x] cannot use non-allowed type '{non_allowed_type}'. Only the following types are allowed: {', '.join(_field_types)}. Got non-allowed type '{non_allowed_type}' with value '{value}'.",
            )

    return instance