Skip to content

Core Utilities

Configuration, utilities, and helper functions.

FhircraftConfig

Path: fhircraft.config.FhircraftConfig

dataclass

FhircraftConfig(disable_validation_warnings: bool = False, disable_fhir_warnings: bool = False, disable_fhir_errors: bool = False, disabled_fhir_constraints: FrozenSet[str] = frozenset(), mode: Literal['strict', 'lenient', 'skip'] = 'strict')

Global configuration for FHIRcraft.

Attributes:

Name Type Description

Parameters:

Name Type Description Default
disable_validation_warnings bool
False
disable_fhir_warnings bool
False
disable_fhir_errors bool
False
disabled_fhir_constraints FrozenSet[str]

Build an immutable unordered collection of unique elements.

<dynamic>
mode Literal['strict', 'lenient', 'skip']
'strict'

configure

configure(*, disable_validation_warnings: bool | None = None, disable_fhir_warnings: bool | None = None, disable_fhir_errors: bool | None = None, disabled_fhir_constraints: Container[str] | None = None, validation_mode: Literal['strict', 'lenient', 'skip'] | None = None) -> None

Configure FHIRcraft settings.

All parameters are optional; only the ones provided will be changed. Unspecified parameters retain their current values.

Parameters:

Name Type Description Default
disable_validation_warnings bool | None

Disable all validation warnings globally.

None
disable_fhir_warnings bool | None

Disable only FHIR warning-severity issues, keep errors.

None
disable_fhir_errors bool | None

Disable error-level constraints (use with extreme caution).

None
disabled_fhir_constraints Container[str] | None

Set of constraint keys to disable (e.g., {'dom-6'}).

None
validation_mode Literal['strict', 'lenient', 'skip'] | None

Validation mode - 'strict', 'lenient', or 'skip'.

None
Source code in fhircraft/config.py
def configure(
    *,
    disable_validation_warnings: bool | None = None,
    disable_fhir_warnings: bool | None = None,
    disable_fhir_errors: bool | None = None,
    disabled_fhir_constraints: Container[str] | None = None,
    validation_mode: Literal["strict", "lenient", "skip"] | None = None,
) -> None:
    """Configure FHIRcraft settings.

    All parameters are optional; only the ones provided will be changed.
    Unspecified parameters retain their current values.

    Args:
        disable_validation_warnings: Disable all validation warnings globally.
        disable_fhir_warnings: Disable only FHIR warning-severity issues, keep errors.
        disable_fhir_errors: Disable error-level constraints (use with extreme caution).
        disabled_fhir_constraints: Set of constraint keys to disable (e.g., {'dom-6'}).
        validation_mode: Validation mode - 'strict', 'lenient', or 'skip'.
    """
    current = get_config()
    _config_context.set(
        dataclasses.replace(
            current,
            **{
                k: v
                for k, v in {
                    "disable_validation_warnings": disable_validation_warnings,
                    "disable_fhir_warnings": disable_fhir_warnings,
                    "disable_fhir_errors": disable_fhir_errors,
                    "disabled_fhir_constraints": disabled_fhir_constraints,
                    "mode": validation_mode,
                }.items()
                if v is not None
            },
        )
    )

disable_constraint

disable_constraint(*constraint_keys: str) -> None

Disable specific validation constraints by their keys.

Creates a new configuration with the given keys added to disabled_fhir_constraints; the change is visible to the current context only and does not escape a surrounding override_config block.

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.

    Creates a new configuration with the given keys added to
    `disabled_fhir_constraints`; the change is visible to the current context
    only and does not escape a surrounding `override_config` block.

    Args:
        *constraint_keys: One or more constraint keys to disable (e.g., 'dom-6').
    """
    config = get_config()
    _config_context.set(
        dataclasses.replace(
            config,
            disabled_fhir_constraints=config.disabled_fhir_constraints
            | frozenset(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()
    _config_context.set(
        dataclasses.replace(
            config,
            disabled_fhir_constraints=config.disabled_fhir_constraints
            - frozenset(constraint_keys),
        )
    )

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.
    """
    config = _config_context.get()
    return config if config is not None else _DEFAULT_CONFIG

load_config_from_env

load_config_from_env() -> None

Load configuration from environment variables.

Supported environment variables
  • FHIRCRAFT_DISABLE_WARNINGS: 'true' to disable all 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 all validation warnings
        - FHIRCRAFT_VALIDATION_MODE: 'strict', 'lenient', or 'skip'
        - FHIRCRAFT_DISABLED_CONSTRAINTS: Comma-separated constraint keys
    """
    kwargs: dict = {}

    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 _VALID_MODES:
        kwargs["validation_mode"] = validation_mode

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

    if kwargs:
        configure(**kwargs)

override_config

override_config(*, disable_validation_warnings: bool | None = None, disable_fhir_warnings: bool | None = None, disable_fhir_errors: bool | None = None, disabled_fhir_constraints: Container[str] | None = None, validation_mode: Literal['strict', 'lenient', 'skip'] | None = None)

Context manager for temporary configuration changes.

All parameters are optional; only the ones provided will be changed within the context block. Previous configuration is automatically restored on exit, even if an exception occurs.

Parameters:

Name Type Description Default
disable_validation_warnings bool | None

Disable all validation warnings globally.

None
disable_fhir_warnings bool | None

Disable only FHIR warning-severity issues, keep errors.

None
disable_fhir_errors bool | None

Disable error-level constraints (use with extreme caution).

None
disabled_fhir_constraints Container[str] | None

Set of constraint keys to disable (e.g., {'dom-6'}).

None
validation_mode Literal['strict', 'lenient', 'skip'] | None

Validation mode - 'strict', 'lenient', or 'skip'.

None

Yields:

Name Type Description
FhircraftConfig

The temporary configuration.

Source code in fhircraft/config.py
@contextmanager
def override_config(
    *,
    disable_validation_warnings: bool | None = None,
    disable_fhir_warnings: bool | None = None,
    disable_fhir_errors: bool | None = None,
    disabled_fhir_constraints: Container[str] | None = None,
    validation_mode: Literal["strict", "lenient", "skip"] | None = None,
):
    """Context manager for temporary configuration changes.

    All parameters are optional; only the ones provided will be changed within
    the context block. Previous configuration is automatically restored on exit,
    even if an exception occurs.

    Args:
        disable_validation_warnings: Disable all validation warnings globally.
        disable_fhir_warnings: Disable only FHIR warning-severity issues, keep errors.
        disable_fhir_errors: Disable error-level constraints (use with extreme caution).
        disabled_fhir_constraints: Set of constraint keys to disable (e.g., {'dom-6'}).
        validation_mode: Validation mode - 'strict', 'lenient', or 'skip'.

    Yields:
        FhircraftConfig: The temporary configuration.
    """
    old_config = get_config()
    new_config = dataclasses.replace(
        old_config,
        **{
            k: v
            for k, v in {
                "disable_validation_warnings": disable_validation_warnings,
                "disable_fhir_warnings": disable_fhir_warnings,
                "disable_fhir_errors": disable_fhir_errors,
                "disabled_fhir_constraints": disabled_fhir_constraints,
                "mode": validation_mode,
            }.items()
            if v is not None
        },
    )
    token = _config_context.set(new_config)
    try:
        yield new_config
    finally:
        _config_context.reset(token)

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.
    """
    _config_context.set(None)

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) -> Literal['DSTU2', 'STU3', 'R4', 'R4B', 'R5', 'R6']
Source code in fhircraft/utils.py
def get_FHIR_release_from_version(
    version: str,
) -> Literal["DSTU2", "STU3", "R4", "R4B", "R5", "R6"]:
    # 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"
    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_dict_subset

is_dict_subset(subset: dict, superset: dict) -> bool

Return True if all keys/values in subset are present in superset.

Source code in fhircraft/utils.py
def is_dict_subset(subset: dict, superset: dict) -> bool:
    """Return True if all keys/values in subset are present in superset."""
    for key, value in subset.items():
        if key not in superset:
            return False
        sup_value = superset[key]
        if isinstance(value, dict) and isinstance(sup_value, dict):
            if not is_dict_subset(value, sup_value):
                return False
        elif isinstance(value, list) and isinstance(sup_value, list):
            if len(value) > len(sup_value):
                return False
            for sub_item, sup_item in zip(value, sup_value):
                if isinstance(sub_item, dict) and isinstance(sup_item, dict):
                    if not is_dict_subset(sub_item, sup_item):
                        return False
                elif sub_item != sup_item:
                    return False
        elif value != sup_value:
            return False
    return True

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."
        )

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

to_snake_case

to_snake_case(name: str) -> str

Convert a given string from CamelCase to snake_case.

Parameters:

Name Type Description Default
name str

The input string in CamelCase format.

required

Returns:

Name Type Description
str str

The converted string in snake_case format.

Source code in fhircraft/utils.py
def to_snake_case(name: str) -> str:
    """
    Convert a given string from CamelCase to snake_case.

    Args:
        name (str): The input string in CamelCase format.

    Returns:
        str: The converted string in snake_case format.
    """
    # Insert underscore before upper-after-lower transitions: "HumanName" -> "Human_Name"
    s = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", name)
    # Insert underscore before final lower of an all-caps run: "URLParser" -> "URL_Parser"
    s = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", s)
    return s.lower()