R/fhir_table_description.R

Defines functions fhir_table_description

Documented in fhir_table_description

#' A S4 class describing the form of a table produced by [fhir_crack()]
#'
#' A `fhir_table_description` holds the information [fhir_crack()] needs to flatten (aka crack) FHIR resources from a FHIR bundle and
#' is created with its constructor function [fhir_table_description()].
#' Each `fhir_table_description` describes a table for a specific resource type as [fhir_crack()] will create one data.frame/data.table
#' per resource type. See Details.
#'
#' @details
#' A `fhir_table_description` consists of
#' the following elements:
#'
#' - The resource element: Defines the resource type (e.g. `Patient` or `Observation`). See [fhir_resource_type()].
#' - The cols element: Contains the column names and XPath expressions defining the columns to extract.
#' If this element is empty, [fhir_crack()] will extract all available elements of the resource and name the
#' columns automatically. See [fhir_columns()].
#' - The sep element: A character of length one containing the separator string used for separating multiple entries in cells.
#' - The brackets element: A character of length one or two used for the indices of multiple entries. The first one is the opening bracket
#' and the second one the closing bracket. Vectors of length one will be recycled.
#' Defaults to `character(0)`, i.e. no brackets, meaning that multiple entries won't be indexed.
#' - The rm_empty_cols element: A logical of length one indicating whether empty columns should be removed in the resulting table or not. Defaults to `FALSE`.
#' - The format element: A character of length one indicating whether the resulting table should be cracked to a `wide` or `compact` format.
#' `wide` means multiple entries will be distributed over several columns with indexed names. `compact` means multiple entries will be pasted into one cell/column separated by `sep` .
#' Defaults to `compact`.
#' - The keep_attr element: A logical of length one indicating whether the attribute name of the respective element (`@value` in most cases)
#' should be attached to the name of the variable in the resulting table. Defaults to `FALSE`.
#'
#' A full `fhir_table_description` looks for example like this:
#' ```
#' fhir_resource_type: Patient
#'
#' fhir_columns:
#' column name | xpath expression
#' ------------------------
#' name        | name/family
#' gender      | gender
#' id          | id
#'
#'sep:           ':::'
#'brackets:      '[', ']'
#'rm_empty_cols: FALSE
#'format:        'compact'
#'keep_attr:     FALSE
#'```
#'
#' @slot resource An object of class [fhir_resource_type-class] defining the resource type that
#' should be extracted.
#' @slot cols An object of class [fhir_columns-class] describing which columns should be created and how.
#' If this is an empty [fhir_columns-class] object, the call to [fhir_crack()] will extract all available
#' elements and put them in automatically named columns.
#' @slot sep A character of length one containing the separator string used for separating multiple entries in cells when `format = "compact"`.
#' ignored when `format = "wide"`.
#' @slot brackets A character of length one or two used for the indices of multiple entries. The first one is the opening bracket
#' and the second one the closing bracket. Vectors of length one will be recycled.
#' Defaults to `character(0)`, i.e. no brackets, meaning that multiple entries won't be indexed.
#' @slot rm_empty_cols A logical of length one indicating whether empty columns should be removed from the resulting table or not. Defaults to FALSE.
#' @slot format A character of length one indicating whether the resulting table should be cracked to a `wide` or `compact` format.
#' `wide` means multiple entries will be distributed over several columns with indexed names. `compact` means multiple entries will be pasted into one cell/column separated by `sep` .
#' Defaults to `compact`.
#' @slot keep_attr A logical of length one indicating whether the attribute name of the respective element (`@value` in most cases)
#' should be attached to the name of the variable in the resulting table. Defaults to `FALSE`
#'
#' @include fhir_resource_type.R fhir_columns.R
#' @seealso [fhir_resource_type()],[fhir_columns()], [fhir_design()], [fhir_crack()]
#' @export
setClass(
	"fhir_table_description",
	slots = c(
		resource      = "fhir_resource_type",
		cols          = "fhir_columns",
		sep           = "character",
		brackets      = "character",
		rm_empty_cols = "logical",
		format        = "character",
		keep_attr     = "logical"
	)
)

setValidity(
	Class  = "fhir_table_description",
	method = function(object) {

		messages <- c()
		if(1 < length(object@sep)) {messages <- c(messages, "sep must be character of length one.")}
		if(!length(object@brackets) %in% c(0, 2)) {messages <- c(messages, "brackets must be character of length two or empty.")}
		if("" %in% object@brackets) {messages <- c(messages, "You cannot use \"\" for brackets.")}
		if(1 < length(object@rm_empty_cols)) {messages <- c(messages, "rm_empty_cols must be logical of length one.")}
		if(!object@format %in% c("wide", "compact")) {messages <- c(messages, "format must be either 'compact' or 'wide'.")}
		if(1 < length(object@keep_attr)) {messages <- c(messages, "keep_attr must be logical of length one.")}
		if(0 < length(messages)) {messages} else {TRUE}
	}
)

#' Create [fhir_table_description-class] object
#'
#' A `fhir_table_description` holds the information [fhir_crack()] needs to flatten (aka crack)
#' FHIR resources from a FHIR bundle. There should be one `fhir_table_description` per resource type as
#' [fhir_crack()] will create one data.frame/data.table per resource type in a bundle. See Details.
#'
#' @details
#' A `fhir_table_description` consists of
#' the following elements:
#'
#' - The resource element: Defines the resource type (e.g. `Patient` or `Observation`). See [fhir_resource_type()].
#' - The cols element: Contains the column names and XPath expressions defining the columns to extract.
#' If this element is empty, [fhir_crack()] will extract all available elements of the resource and name the
#' columns automatically. See [fhir_columns()].
#' - The sep element: A character of length one containing the separator string used for separating multiple entries in cells.
#' - The brackets element: A character of length one or two used for the indices of multiple entries. The first one is the opening bracket
#' and the second one the closing bracket. Vectors of length one will be recycled.
#' Defaults to `character(0)`, i.e. no brackets, meaning that multiple entries won't be indexed.
#' - The rm_empty_cols element: A logical of length one indicating whether empty columns should be removed in the resulting table or not. Defaults to `FALSE`.
#' - The format element: A character of length one indicating whether the resulting table should be cracked to a `wide` or `compact` format.
#' `wide` means multiple entries will be distributed over several columns with indexed names. `compact` means multiple entries will be pasted into one cell/column separated by `sep` .
#' Defaults to `compact`.
#' - The keep_attr element: A logical of length one indicating whether the attribute name of the respective element (`@value` in most cases)
#' should be attached to the name of the variable in the resulting table. Defaults to `FALSE`.
#'
#'
#' A full `fhir_table_description` looks for example like this:
#' ```
#' fhir_resource_type: Patient
#'
#' fhir_columns:
#' column name | xpath expression
#' ------------------------
#' name        | name/family
#' gender      | gender
#' id          | id
#'
#'sep:           ':::'
#'brackets:      '[', ']'
#'rm_empty_cols: FALSE
#'format:        'compact'
#'keep_attr:     FALSE
#'```
#'
#' @param resource A character vector of length one or [fhir_resource_type-class] object
#' indicating which resource type should be extracted.
#' @param cols Optional. A [fhir_columns-class] object or something that can be coerced to one,
#' like a (named) character vector, a (named) list containing xpath expressions,
#' or a [fhir_xpath_expression-class] object. See [fhir_columns()] and the examples.
#' If this argument is omitted, an empty [fhir_columns-class] object will be supplied.
#' This means that in the call to [fhir_crack()], all available elements are extracted in put
#' in automatically named columns.
#' @param sep  A character of length one containing the separator string used for separating multiple entries in cells when `format = "compact"`.
#' ignored when `format = "wide"`. Defaults to `":::"`.
#' @param brackets A character of length one or two used for the indices of multiple entries. The first one is the opening bracket
#' and the second one the closing bracket. Vectors of length one will be recycled.
#' Defaults to `character(0)`, i.e. no brackets, meaning that multiple entries won't be indexed.
#' @param rm_empty_cols A logical of length one indicating whether empty columns should be removed from the resulting table or not. Defaults to `FALSE`.
#' @param format  A character of length one indicating whether the resulting table should be cracked to a `"wide"` or `"compact"` format.
#' `"wide"` means multiple entries will be distributed over several columns with indexed names. `"compact"` means multiple entries will be pasted into one cell/column separated by `sep` .
#' Defaults to `"compact"`.
#' @param keep_attr A logical of length one indicating whether the attribute name of the respective element (`@value` in most cases)
#' should be attached to the name of the variable in the resulting table. Defaults to `FALSE`.
#' @param style `r lifecycle::badge("deprecated")`
#' @return An object of class [fhir_table_description-class].
#'
#' @examples
#' # a minimal table description
#' fhir_table_description(
#'     resource = "Patient"
#' )
#'
#' # named list for cols
#' fhir_table_description(
#'     resource = "Patient",
#'     cols     = list(
#'         id     = "id",
#'         name   = "name/family",
#'         gender = "gender"
#'     )
#' )
#'
#' #unnamed character for cols, colnames are generated automatically
#' fhir_table_description(
#'     resource = "Patient",
#'     cols     = c(
#'         "id",
#'         "name/family",
#'         "gender"
#'     )
#' )
#'
#' # named character for cols, and overwritten default for other arguments
#' fhir_table_description(
#'     resource = "Patient",
#'     cols = c(
#'         id            = "id",
#'         name          = "name/family",
#'         gender        = "gender"
#'     ),
#'     brackets      = c("[", "]"),
#'     rm_empty_cols = TRUE,
#'     format        = "wide"
#' )
#'
#' # no column arguments is given -> creates a column for all available elements
#' fhir_table_description(
#'     resource = "Patient",
#'     sep           = " <~> ",
#'     brackets      = c("<<<", ">>>"),
#'     rm_empty_cols = FALSE,
#'     format        = "wide"
#' )
#'
#' @export
fhir_table_description <- function(
	resource,
	cols          = fhir_columns(),
	sep           = ':::',
	brackets      = character(),
	rm_empty_cols = FALSE,
	format        = 'compact',
	keep_attr     = FALSE,
	style         = deprecated()
	) {

	if(lifecycle::is_present(style)){
		lifecycle::deprecate_warn(when = "2.0.0", what = "fhir_table_description(style)",
								  details = "Please code style elements directly in the table description. Overwriting fhir_table_descriptions arguments for now.\n")
		sep <- style@sep
		brackets <- style@brackets
		rm_empty_cols <- style@rm_empty_cols
	}

	resource <- fhir_resource_type(string = resource)

	brackets <- fix_brackets(brackets = brackets)

	if(!inherits(cols, 'fhir_columns')) {
		cols <- fhir_columns(xpaths = cols)
	}

	new(
		Class         = 'fhir_table_description',
		resource      = resource,
		cols          = cols,
		sep           = sep,
		brackets      = brackets,
		rm_empty_cols = rm_empty_cols,
		format        = format,
		keep_attr     = keep_attr
	)
}

setMethod(
	f = 'show',
	signature = 'fhir_table_description',
	function(object) {
		cat('A fhir_table_description with the following elements: \n\n')
		cat(paste0('resource: ', as.character(object@resource), '\n\n'))
		cat('cols: \n');
		show(object@cols)
		cat(paste0('\nsep:           \'', object@sep, '\'\n'))
		if(length(object@brackets) < 1) {
			cat('brackets:      no brackets\n')
		} else {
			cat(paste0('brackets:      \'', object@brackets[1], '\', \'', object@brackets[2], '\'\n'))
		}
		cat(paste0('rm_empty_cols: ', object@rm_empty_cols, '\n'))
		cat(paste0('format:        \'', object@format, '\'\n'))
		cat(paste0('keep_attr:     ', object@keep_attr, '\n'))
	}
)
TPeschel/fhiR documentation built on April 14, 2024, 7:31 a.m.