Skip to content

Core Utilities

Configuration, utilities, and helper functions.

FhircraftConfig

Path: fhircraft.config.FhircraftConfig

dataclass

FhircraftConfig(validation: ValidationConfig = ValidationConfig())

Global configuration for FHIRcraft.

This configuration class is designed to be easily extendable for future features beyond validation.

Attributes:

Name Type Description

Parameters:

Name Type Description Default
validation ValidationConfig

Configuration for FHIR validation behavior.

Attributes: disable_warnings: Disable all validation warnings globally. disabled_constraints: Set of constraint keys to disable (e.g., 'dom-6'). disable_warning_severity: Disable only warnings, keep errors. disable_errors: Disable error-level constraints (use with extreme caution). mode: Validation mode - 'strict', 'lenient', or 'skip'. - strict: All validations enabled (default) - lenient: Convert errors to warnings - skip: Disable all validations

<dynamic>

ValidationConfig

Path: fhircraft.config.ValidationConfig

dataclass

ValidationConfig(disable_warnings: bool = False, disabled_constraints: Set[str] = set(), disable_warning_severity: bool = False, disable_errors: bool = False, mode: Literal['strict', 'lenient', 'skip'] = 'strict')

Configuration for FHIR validation behavior.

Attributes:

Name Type Description

Parameters:

Name Type Description Default
disable_warnings bool
False
disabled_constraints Set[str]

Build an unordered collection of unique elements.

<dynamic>
disable_warning_severity bool
False
disable_errors bool
False
mode Literal['strict', 'lenient', 'skip']
'strict'

configure

configure(**kwargs) -> None

Configure FHIRcraft settings using keyword arguments.

This is a convenience function that allows setting configuration options without creating config objects explicitly.

**kwargs: Configuration options. Can include: - disable_validation_warnings (bool) - validation_mode (str: 'strict', 'lenient', 'skip') - disable_validation_errors (bool) - disabled_constraints (Set[str])

Source code in fhircraft/config.py
def configure(**kwargs) -> None:
    """Configure FHIRcraft settings using keyword arguments.

    This is a convenience function that allows setting configuration
    options without creating config objects explicitly.

    **kwargs: Configuration options. Can include:
        - disable_validation_warnings (bool)
        - validation_mode (str: 'strict', 'lenient', 'skip')
        - disable_validation_errors (bool)
        - disabled_constraints (Set[str])
    """
    current_config = get_config()

    # Map convenience kwargs to nested structure
    validation_kwargs = {}

    if "disable_validation_warnings" in kwargs:
        validation_kwargs["disable_warnings"] = kwargs["disable_validation_warnings"]
    if "validation_mode" in kwargs:
        validation_kwargs["mode"] = kwargs["validation_mode"]
    if "disable_validation_errors" in kwargs:
        validation_kwargs["disable_errors"] = kwargs["disable_validation_errors"]
    if "disabled_constraints" in kwargs:
        validation_kwargs["disabled_constraints"] = kwargs["disabled_constraints"]
    if "disable_warning_severity" in kwargs:
        validation_kwargs["disable_warning_severity"] = kwargs[
            "disable_warning_severity"
        ]

    # Create new validation config with updates
    validation_dict = {**current_config.validation.__dict__, **validation_kwargs}
    new_validation = ValidationConfig(**validation_dict)

    # Create new config
    new_config = FhircraftConfig(validation=new_validation)
    set_config(new_config)

disable_constraint

disable_constraint(*constraint_keys: str) -> None

Disable specific validation constraints by their keys.

Parameters:

Name Type Description Default
*constraint_keys str

One or more constraint keys to disable (e.g., 'dom-6').

()
Source code in fhircraft/config.py
def disable_constraint(*constraint_keys: str) -> None:
    """Disable specific validation constraints by their keys.

    Args:
        *constraint_keys: One or more constraint keys to disable (e.g., 'dom-6').
    """
    config = get_config()
    config.validation.disabled_constraints.update(constraint_keys)

enable_constraint

enable_constraint(*constraint_keys: str) -> None

Re-enable specific validation constraints by their keys.

Parameters:

Name Type Description Default
*constraint_keys str

One or more constraint keys to re-enable.

()
Source code in fhircraft/config.py
def enable_constraint(*constraint_keys: str) -> None:
    """Re-enable specific validation constraints by their keys.

    Args:
        *constraint_keys: One or more constraint keys to re-enable.
    """
    config = get_config()
    for key in constraint_keys:
        config.validation.disabled_constraints.discard(key)

get_config

get_config() -> FhircraftConfig

Get the current FHIRcraft configuration.

Returns:

Name Type Description
FhircraftConfig FhircraftConfig

The current configuration instance.

Source code in fhircraft/config.py
def get_config() -> FhircraftConfig:
    """Get the current FHIRcraft configuration.

    Returns:
        FhircraftConfig: The current configuration instance.
    """
    return _config_context.get()

load_config_from_env

load_config_from_env() -> None

Load configuration from environment variables.

Supported environment variables
  • FHIRCRAFT_DISABLE_WARNINGS: 'true' to disable validation warnings
  • FHIRCRAFT_VALIDATION_MODE: 'strict', 'lenient', or 'skip'
  • FHIRCRAFT_DISABLED_CONSTRAINTS: Comma-separated constraint keys
Source code in fhircraft/config.py
def load_config_from_env() -> None:
    """Load configuration from environment variables.

    Supported environment variables:
        - FHIRCRAFT_DISABLE_WARNINGS: 'true' to disable validation warnings
        - FHIRCRAFT_VALIDATION_MODE: 'strict', 'lenient', or 'skip'
        - FHIRCRAFT_DISABLED_CONSTRAINTS: Comma-separated constraint keys
    """
    kwargs = {}

    if os.getenv("FHIRCRAFT_DISABLE_WARNINGS", "").lower() == "true":
        kwargs["disable_validation_warnings"] = True

    validation_mode = os.getenv("FHIRCRAFT_VALIDATION_MODE", "").lower()
    if validation_mode in ("strict", "lenient", "skip"):
        kwargs["validation_mode"] = validation_mode

    disabled_constraints = os.getenv("FHIRCRAFT_DISABLED_CONSTRAINTS", "")
    if disabled_constraints:
        kwargs["disabled_constraints"] = set(
            key.strip() for key in disabled_constraints.split(",")
        )

    if kwargs:
        configure(**kwargs)

reset_config

reset_config() -> None

Reset configuration to default values.

This is useful for testing or when you want to clear all configuration changes.

Source code in fhircraft/config.py
def reset_config() -> None:
    """Reset configuration to default values.

    This is useful for testing or when you want to clear all
    configuration changes.
    """
    set_config(FhircraftConfig())

set_config

set_config(config: FhircraftConfig) -> None

Set the global FHIRcraft configuration.

Parameters:

Name Type Description Default
config FhircraftConfig

The new configuration to apply globally.

required
Warning

This sets the configuration globally and persists until changed. Consider using with_config() for temporary changes.

Source code in fhircraft/config.py
def set_config(config: FhircraftConfig) -> None:
    """Set the global FHIRcraft configuration.

    Args:
        config: The new configuration to apply globally.

    Warning:
        This sets the configuration globally and persists until changed.
        Consider using `with_config()` for temporary changes.
    """
    _config_context.set(config)

with_config

with_config(**kwargs)

Context manager for temporary configuration changes.

This allows you to temporarily modify configuration within a specific code block, with automatic restoration when exiting the block.

Yields:

Type Description
FhircraftConfig

The temporary configuration.

Source code in fhircraft/config.py
@contextmanager
def with_config(**kwargs):
    """Context manager for temporary configuration changes.

    This allows you to temporarily modify configuration within a specific
    code block, with automatic restoration when exiting the block.

    Yields:
        (FhircraftConfig): The temporary configuration.
    """
    old_config = get_config()

    # Build new config from kwargs
    validation_kwargs = {}
    if "disable_validation_warnings" in kwargs:
        validation_kwargs["disable_warnings"] = kwargs["disable_validation_warnings"]
    if "validation_mode" in kwargs:
        validation_kwargs["mode"] = kwargs["validation_mode"]
    if "disable_validation_errors" in kwargs:
        validation_kwargs["disable_errors"] = kwargs["disable_validation_errors"]
    if "disabled_constraints" in kwargs:
        validation_kwargs["disabled_constraints"] = kwargs["disabled_constraints"]
    if "disable_warning_severity" in kwargs:
        validation_kwargs["disable_warning_severity"] = kwargs[
            "disable_warning_severity"
        ]

    # Merge with current config
    validation_dict = {**old_config.validation.__dict__, **validation_kwargs}
    new_validation = ValidationConfig(**validation_dict)
    new_config = FhircraftConfig(validation=new_validation)

    # Set new config and store token for reset
    token = _config_context.set(new_config)
    try:
        yield new_config
    finally:
        _config_context.reset(token)

T module-attribute

T = TypeVar('T')

T_ module-attribute

T_ = TypeVar('T_')

capitalize

capitalize(string: str) -> str

Capitalize the first letter of a given string.

Parameters:

Name Type Description Default
string str

The input string to capitalize.

required

Returns:

Name Type Description
str str

The string with the first letter capitalized.

Source code in fhircraft/utils.py
def capitalize(string: str) -> str:
    """
    Capitalize the first letter of a given string.

    Parameters:
        string (str): The input string to capitalize.

    Returns:
        str: The string with the first letter capitalized.
    """
    return string[0].upper() + string[1:]

contains_list_type

contains_list_type(tp: Any) -> bool

Recursively check if List is anywhere in the variable's typing.

Source code in fhircraft/utils.py
def contains_list_type(tp: Any) -> bool:
    """Recursively check if List is anywhere in the variable's typing."""
    # Check if the current type is a List
    if get_origin(tp) in [List, list]:
        return True

    # Recursively check the type arguments
    for arg in get_args(tp):
        if contains_list_type(arg):
            return True

    return False

contains_only_none

contains_only_none(d: Any) -> bool

Check if the input contains only None values recursively.

Parameters:

Name Type Description Default
d Any

The input dictionary or list to check for only None values.

required

Returns:

Name Type Description
result bool

True if the input contains only None values, False otherwise.

Source code in fhircraft/utils.py
def contains_only_none(d: Any) -> bool:
    """
    Check if the input contains only None values recursively.

    Args:
        d (Any): The input dictionary or list to check for only None values.

    Returns:
        result (bool): True if the input contains only None values, False otherwise.
    """
    if isinstance(d, dict):
        return all(contains_only_none(v) for v in d.values())
    elif isinstance(d, list):
        return all(contains_only_none(item) for item in d)
    else:
        return d is None

ensure_list

ensure_list(variable: Any) -> list

Ensure that the input variable is converted into a list if it is not already an iterable.

Parameters:

Name Type Description Default
variable any

The input variable that needs to be converted into a list if it is not already an iterable.

required

Returns:

Name Type Description
variable list

The input variable converted into a list, or the input variable itself if it was already an iterable.

Source code in fhircraft/utils.py
def ensure_list(variable: Any) -> list:
    """
    Ensure that the input variable is converted into a list if it is not already an iterable.

    Args:
        variable (any): The input variable that needs to be converted into a list if it is not already an iterable.

    Returns:
        variable (list): The input variable converted into a list, or the input variable itself if it was already an iterable.
    """
    if not isinstance(variable, list):
        if isinstance(variable, tuple):
            return list(variable)
        return [variable]
    return variable

get_FHIR_release_from_version

get_FHIR_release_from_version(version: str) -> str
Source code in fhircraft/utils.py
def get_FHIR_release_from_version(version: str) -> str:
    # Check format of the version string
    if not re.match(r"^\d+\.\d+\.\d+$", version):
        raise ValueError(f'FHIR version must be in "x.y.z" format, got "{version}"')
    # Parse version string into a three-digit tuple
    version = version.split("-")[0]
    version_tuple = tuple([int(digit) for digit in version.split(".")])
    # Assign FHIR release based on version number (Referece: http://hl7.org/fhir/directory.html)
    if version_tuple >= (0, 4, 0) and version_tuple <= (1, 0, 2):
        return "DSTU2"
    elif version_tuple >= (1, 1, 0) and version_tuple <= (3, 0, 2):
        return "STU3"
    elif version_tuple >= (3, 2, 0) and version_tuple <= (4, 0, 1):
        return "R4"
    elif version_tuple >= (4, 1, 0) and version_tuple <= (4, 3, 0):
        return "R4B"
    elif version_tuple >= (4, 2, 0) and version_tuple <= (5, 0, 0):
        return "R5"
    elif version_tuple >= (6, 0, 0):
        return "R6"
    elif version_tuple >= (3, 2, 0) and version_tuple <= (4, 0, 1):
        return "R4"
    elif version_tuple >= (4, 1, 0) and version_tuple <= (4, 3, 0):
        return "R4B"
    elif version_tuple >= (4, 2, 0) and version_tuple <= (5, 0, 0):
        return "R5"
    elif version_tuple >= (6, 0, 0):
        return "R6"
    else:
        raise ValueError(
            f"FHIR version {version} is not supported. Supported versions are: "
            "DSTU2, STU3, R4, R4B, R5, and R6."
        )

get_all_models_from_field

get_all_models_from_field(field: FieldInfo, issubclass_of: type[T_] = BaseModel) -> Generator[Type[T_], None, None]
Source code in fhircraft/utils.py
def get_all_models_from_field(
    field: FieldInfo, issubclass_of: type[T_] = BaseModel
) -> Generator[Type[T_], None, None]:
    return (
        arg
        for arg in _get_deepest_args(field.annotation)
        if inspect.isclass(arg) and issubclass(arg, issubclass_of)
    )

get_dict_paths

get_dict_paths(nested_dict: Union[Dict[str, Any], List[Dict[str, Any]]], prefix: str = '') -> Dict[str, Any]

Get all paths in a nested dictionary with their corresponding values.

Parameters:

Name Type Description Default
nested_dict Union[Dict[str, Any], List[Dict[str, Any]]]

The nested dictionary or list of dictionaries to extract paths from.

required
prefix str

The prefix to be added to the paths (default is '').

''

Returns:

Type Description
Dict[str, Any]

Dict[str, Any]: A dictionary containing all paths in the nested dictionary with their corresponding values.

Source code in fhircraft/utils.py
def get_dict_paths(
    nested_dict: Union[Dict[str, Any], List[Dict[str, Any]]], prefix: str = ""
) -> Dict[str, Any]:
    """
    Get all paths in a nested dictionary with their corresponding values.

    Args:
        nested_dict (Union[Dict[str, Any], List[Dict[str, Any]]]): The nested dictionary or list of dictionaries to extract paths from.
        prefix (str): The prefix to be added to the paths (default is '').

    Returns:
        Dict[str, Any]: A dictionary containing all paths in the nested dictionary with their corresponding values.
    """
    paths = {}
    if isinstance(nested_dict, dict):
        for key, value in nested_dict.items():
            new_prefix = f"{prefix}.{key}" if prefix else key
            if isinstance(value, dict):
                paths.update(get_dict_paths(value, new_prefix))
            elif isinstance(value, list):
                for i, item in enumerate(value):
                    list_prefix = f"{new_prefix}[{i}]"
                    if isinstance(item, dict):
                        paths.update(get_dict_paths(item, list_prefix))
            else:
                if value is not None:
                    paths[new_prefix] = value
    elif isinstance(nested_dict, list):
        for i, item in enumerate(nested_dict):
            list_prefix = f"{prefix}[{i}]"
            if isinstance(item, dict):
                paths.update(get_dict_paths(item, list_prefix))
    return paths

get_fhir_model_from_field

get_fhir_model_from_field(field: FieldInfo) -> type[BaseModel] | None
Source code in fhircraft/utils.py
def get_fhir_model_from_field(field: FieldInfo) -> type[BaseModel] | None:
    return next(get_all_models_from_field(field), None)

get_module_name

get_module_name(obj: Any) -> str

Returns the name of the module to which the given object belongs.

Parameters:

Name Type Description Default
obj Any

The object whose module name is to be retrieved.

required

Returns:

Name Type Description
str str

The name of the module containing the object.

Raises:

Type Description
ValueError

If the object does not belong to any module.

Source code in fhircraft/utils.py
def get_module_name(obj: Any) -> str:
    """
    Returns the name of the module to which the given object belongs.

    Args:
        obj (Any): The object whose module name is to be retrieved.

    Returns:
        str: The name of the module containing the object.

    Raises:
        ValueError: If the object does not belong to any module.
    """
    module = inspect.getmodule(obj)
    if module is None:
        raise ValueError(f"The object {obj} does not belong to a module")
    return module.__name__

is_list_field

is_list_field(field) -> bool

Determines if a given Pydantic field or FieldInfo is a list type, including Optional[List[T]] and Union[List[T], ...].

Source code in fhircraft/utils.py
def is_list_field(field) -> bool:
    """
    Determines if a given Pydantic field or FieldInfo is a list type,
    including Optional[List[T]] and Union[List[T], ...].
    """
    annotation = getattr(field, "annotation", None)
    if annotation is None:
        annotation = getattr(field, "outer_type_", None)
    if annotation is None:
        annotation = getattr(field, "type_", None)
    if annotation is None:
        return False

    def _is_list_type(ann):
        origin = getattr(ann, "__origin__", None)
        if origin in (list, List):
            return True
        if origin is Union:
            return any(_is_list_type(arg) for arg in getattr(ann, "__args__", ()))
        return False

    return _is_list_type(annotation)

is_url

is_url(string: str) -> bool

Check if the input string is a valid URL.

Parameters:

Name Type Description Default
string str

The input string to check.

required

Returns:

Name Type Description
bool bool

True if the input string is a valid URL, False otherwise.

Source code in fhircraft/utils.py
def is_url(string: str) -> bool:
    """Check if the input string is a valid URL.

    Args:
        string (str): The input string to check.

    Returns:
        bool: True if the input string is a valid URL, False otherwise.
    """
    return re.match(URL_PATTERNS, string) is not None

load_env_variables

load_env_variables(file_path: Optional[str] = None) -> dict

Loads environment variables from a .env file into a dictionary without changing the global environment variables.

Parameters:

Name Type Description Default
file_path Optional[str]

Optional path to the .env file. If not provided, it looks for a .env file in the current directory.

None

Returns:

Name Type Description
vars dict

A dictionary containing the environment variables from the .env file.

Source code in fhircraft/utils.py
def load_env_variables(file_path: Optional[str] = None) -> dict:
    """
    Loads environment variables from a .env file into a dictionary without changing the global environment variables.

    Args:
        file_path (Optional[str]): Optional path to the .env file. If not provided, it looks for a .env file in the current directory.

    Returns:
        vars (dict): A dictionary containing the environment variables from the .env file.
    """
    # Determine the file path
    env_file = file_path if file_path else ".env"

    # Load the .env file into a dictionary
    env_vars = dotenv_values(env_file)

    return env_vars

load_file

load_file(file_path: str) -> Dict

Load data from a file based on its extension.

Parameters:

Name Type Description Default
file_path str

The path to the file to load.

required

Returns:

Name Type Description
data dict

The data loaded from the file as a dictionary.

Raises:

Type Description
ValueError

If the file content is not a dictionary (for YAML files).

Source code in fhircraft/utils.py
def load_file(file_path: str) -> Dict:
    """
    Load data from a file based on its extension.

    Args:
        file_path (str): The path to the file to load.

    Returns:
        data (dict): The data loaded from the file as a dictionary.

    Raises:
        ValueError: If the file content is not a dictionary (for YAML files).
    """
    with open(file_path, "r") as file:
        file_extension = os.path.splitext(file_path)[1]
        if file_extension == ".yaml" or file_extension == ".yml":
            data = yaml.safe_load(file)
            if not isinstance(data, dict):
                raise ValueError(
                    "Invalid file content. File content must be a dictionary."
                )
            return data
        elif file_extension == ".json":
            return json.load(file)
        else:
            raise ValueError(
                "Unsupported file format. Please provide a .yaml, .yml, or .json file."
            )

load_url

load_url(url: str) -> Dict

Load content from a URL and parse it based on the content type (YAML or JSON).

Parameters:

Name Type Description Default
url str

The URL to load content from.

required

Returns:

Type Description
Dict

Union[Dict, List, Any]: Parsed content from the URL. Can be a dictionary, list, or any other valid JSON/YAML data type.

Raises:

Type Description
ValueError

If the URL format is invalid or the content type is not supported.

Source code in fhircraft/utils.py
def load_url(url: str) -> Dict:
    """
    Load content from a URL and parse it based on the content type (YAML or JSON).

    Args:
        url (str): The URL to load content from.

    Returns:
        Union[Dict, List, Any]: Parsed content from the URL. Can be a dictionary, list, or any other valid JSON/YAML data type.

    Raises:
        ValueError: If the URL format is invalid or the content type is not supported.
    """
    # Validate the URL format
    if not url.startswith("http://") and not url.startswith("https://"):
        raise ValueError(
            "Invalid URL format. Please provide a valid URL starting with 'http://' or 'https://'."
        )

    # Add a timeout to the requests.get call
    # Configure proxy if needed
    settings = load_env_variables()
    proxies = None
    if settings.get("PROXY_URL_HTTPS") or settings.get("PROXY_URL_HTTP"):
        # Only include keys with non-None string values
        proxies = {}
        if settings.get("PROXY_URL_HTTPS") is not None:
            proxies["https"] = str(settings.get("PROXY_URL_HTTPS"))
        if settings.get("PROXY_URL_HTTP") is not None:
            proxies["http"] = str(settings.get("PROXY_URL_HTTP"))
        if not proxies:
            proxies = None
    # Download the StructureDefinition JSON
    response = requests.get(
        url, proxies=proxies, verify=settings.get("CERTIFICATE_BUNDLE_PATH"), timeout=10
    )

    response.raise_for_status()
    content_type = response.headers["Content-Type"]

    # Use content_type.lower() to make the content type check case-insensitive
    if "yaml" in content_type.lower():
        try:
            return yaml.safe_load(response.text)
        except yaml.YAMLError as e:
            raise ValueError(f"Error loading YAML: {e}")
    elif "json" in content_type.lower():
        try:
            return response.json()
        except json.JSONDecodeError as e:
            raise ValueError(f"Error loading JSON: {e}")
    else:
        raise ValueError(
            "Unsupported content type. Please provide a URL that returns .yaml, .yml, or .json content."
        )

merge_dicts

merge_dicts(dict1: dict, dict2: dict) -> dict

Merge two dictionaries recursively, merging lists element by element and dictionaries at the same index.

If a key exists in both dictionaries, the values are merged based on their types. If a key exists only in one dictionary, it is added to the merged dictionary.

Parameters:

Name Type Description Default
dict1 dict

The first dictionary to merge.

required
dict2 dict

The second dictionary to merge.

required

Returns:

Name Type Description
dict dict

The merged dictionary.

Example

dict1 = {'a': 1, 'b': {'c': 2, 'd': [3, 4]}, 'e': [5, 6]} dict2 = {'b': {'c': 3, 'd': [4, 5]}, 'e': [6, 7], 'f': 8} merge_dicts(dict1, dict2) {'a': 1, 'b': {'c': 3, 'd': [3, 4, 5]}, 'e': [5, 6, 7], 'f': 8}

Source code in fhircraft/utils.py
def merge_dicts(dict1: dict, dict2: dict) -> dict:
    """
    Merge two dictionaries recursively, merging lists element by element and dictionaries at the same index.

    If a key exists in both dictionaries, the values are merged based on their types. If a key exists only in one dictionary, it is added to the merged dictionary.

    Args:
        dict1 (dict): The first dictionary to merge.
        dict2 (dict): The second dictionary to merge.

    Returns:
        dict: The merged dictionary.

    Example:
        >>> dict1 = {'a': 1, 'b': {'c': 2, 'd': [3, 4]}, 'e': [5, 6]}
        >>> dict2 = {'b': {'c': 3, 'd': [4, 5]}, 'e': [6, 7], 'f': 8}
        >>> merge_dicts(dict1, dict2)
        {'a': 1, 'b': {'c': 3, 'd': [3, 4, 5]}, 'e': [5, 6, 7], 'f': 8}
    """

    def merge_lists(list1, list2):
        # Merge two lists element by element
        merged_list = []
        for idx in range(max(len(list1), len(list2))):
            if idx < len(list1) and idx < len(list2):
                if isinstance(list1[idx], dict) and isinstance(list2[idx], dict):
                    # Merge dictionaries at the same index
                    merged_list.append(merge_dicts(list1[idx], list2[idx]))
                else:
                    # If they are not dictionaries, choose the element from the first list
                    merged_list.append(list1[idx])
            elif idx < len(list1):
                merged_list.append(list1[idx])
            else:
                merged_list.append(list2[idx])
        return merged_list

    merged_dict = dict1.copy()
    for key, value in dict2.items():
        if key in merged_dict:
            if isinstance(merged_dict[key], list) and isinstance(value, list):
                merged_dict[key] = merge_lists(merged_dict[key], value)
            elif isinstance(merged_dict[key], dict) and isinstance(value, dict):
                merged_dict[key] = merge_dicts(merged_dict[key], value)
            else:
                merged_dict[key] = value
        else:
            merged_dict[key] = value
    return merged_dict

model_rebuild_all

model_rebuild_all()

Call model_rebuild() on all Pydantic models defined in the module where this function is called. This is useful when models have forward references or need to be re-evaluated after all classes have been defined.

Source code in fhircraft/utils.py
def model_rebuild_all():
    """
    Call model_rebuild() on all Pydantic models defined in the module where this function is called.
    This is useful when models have forward references or need to be re-evaluated
    after all classes have been defined.
    """
    caller_module_name = inspect.currentframe().f_back.f_globals["__name__"]  # type: ignore
    for name, obj in inspect.getmembers(
        sys.modules[caller_module_name], inspect.isclass
    ):
        # Only call model_rebuild for classes defined in the caller's module
        if obj.__module__ == caller_module_name and hasattr(obj, "model_rebuild"):
            obj.model_rebuild()

remove_none_dicts

remove_none_dicts(d: Union[Dict[str, Any], List[Any], Any]) -> Union[Dict[str, Any], List[Any], Any]

Remove any dictionaries with all values being None from the input dictionary recursively.

Parameters:

Name Type Description Default
d Union[Dict[str, Any], List[Any], Any]

The input dictionary or list to remove None values from.

required

Returns:

Type Description
Union[Dict[str, Any], List[Any], Any]

Union[Dict[str, Any], List[Any], Any]: The dictionary or list with None values removed.

Source code in fhircraft/utils.py
def remove_none_dicts(
    d: Union[Dict[str, Any], List[Any], Any],
) -> Union[Dict[str, Any], List[Any], Any]:
    """
    Remove any dictionaries with all values being None from the input dictionary recursively.

    Args:
        d (Union[Dict[str, Any], List[Any], Any]): The input dictionary or list to remove None values from.

    Returns:
        Union[Dict[str, Any], List[Any], Any]: The dictionary or list with None values removed.
    """
    if not isinstance(d, dict):
        return d
    new_dict = {}
    for k, v in d.items():
        if isinstance(v, dict):
            v = remove_none_dicts(v)
            if not contains_only_none(v):
                new_dict[k] = v
        elif isinstance(v, list):
            new_list = []
            for item in v:
                if isinstance(item, dict):
                    item = remove_none_dicts(item)
                    if not contains_only_none(item):
                        new_list.append(item)
                elif item is not None:
                    new_list.append(item)
            if new_list:
                new_dict[k] = new_list
        elif v is not None:
            new_dict[k] = v
    return new_dict

replace_nth

replace_nth(string, sub, wanted, n)

Replace the nth occurrence of a substring in a string.

Parameters:

Name Type Description Default
string str

The original string.

required
sub str

The substring to be replaced.

required
wanted str

The new substring to replace with.

required
n int

The occurrence number of the substring to replace.

required

Returns:

Name Type Description
string str

The updated string after replacing the nth occurrence of the substring.

Source code in fhircraft/utils.py
def replace_nth(string, sub, wanted, n):
    """
    Replace the nth occurrence of a substring in a string.

    Args:
        string (str): The original string.
        sub (str): The substring to be replaced.
        wanted (str): The new substring to replace with.
        n (int): The occurrence number of the substring to replace.

    Returns:
        string (str): The updated string after replacing the nth occurrence of the substring.
    """
    pattern = re.compile(sub)
    where = [m for m in pattern.finditer(string)][n - 1]
    before = string[: where.start()]
    after = string[where.end() :]
    newString = before + wanted + after
    return newString