diff --git a/DESCRIPTION b/DESCRIPTION index c3931470..4d2399d1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -52,6 +52,7 @@ Collate: 'data_inventory_table.R' 'demographics-table.R' 'discrete_table.R' + 'glossary.R' 'knit-dependencies.R' 'preview-standalone.R' 'preview.R' diff --git a/NAMESPACE b/NAMESPACE index ddef5346..7381d47d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,10 @@ # Generated by roxygen2: do not edit by hand +S3method("[",glossary) +S3method(as.data.frame,glossary) S3method(as.list,digits) +S3method(as.list,glossary) +S3method(as.list,glossary_entry) S3method(as.panel,"NULL") S3method(as.panel,character) S3method(as.panel,quosures) @@ -8,12 +12,18 @@ S3method(as.panel,rowpanel) S3method(as_stable,pmtable) S3method(as_stable,stable) S3method(as_stable,stobject) +S3method(c,glossary) +S3method(glossary_notes,character) +S3method(glossary_notes,glossary) +S3method(glossary_notes,list) S3method(names,stobject) S3method(new_names,character) S3method(new_names,list) S3method(new_names,quosures) S3method(new_names,rowpanel) S3method(print,digits) +S3method(print,glossary) +S3method(print,glossary_entry) S3method(print,st_caption) S3method(print,stable_data) S3method(print,stobject) @@ -38,6 +48,7 @@ export(.cols) export(as.caption) export(as.panel) export(as.span) +export(as_glossary) export(as_lscape) export(as_stable) export(cat_data) @@ -59,6 +70,7 @@ export(digit1) export(ensure_parens) export(find_bq_col) export(get_stable_data) +export(glossary_notes) export(is.colgroup) export(is.colsplit) export(is.rowpanel) @@ -80,8 +92,10 @@ export(pt_data_study) export(pt_demographics) export(pt_wrap) export(ptdata) +export(read_glossary) export(rnd) export(rowpanel) +export(select_glossary) export(sig) export(st2article) export(st2doc) @@ -122,6 +136,7 @@ export(st_notes) export(st_notes_app) export(st_notes_conf) export(st_notes_detach) +export(st_notes_glo) export(st_notes_rm) export(st_notes_str) export(st_notes_sub) @@ -156,6 +171,7 @@ export(tab_spanners) export(tex_bold) export(tex_it) export(triage_data) +export(update_abbrev) export(yaml_as_df) importFrom(assertthat,assert_that) importFrom(assertthat,validate_that) @@ -205,10 +221,12 @@ importFrom(purrr,walk) importFrom(rlang,":=") importFrom(rlang,.data) importFrom(rlang,.env) +importFrom(rlang,abort) importFrom(rlang,as_string) importFrom(rlang,enquo) importFrom(rlang,enquos) importFrom(rlang,env_clone) +importFrom(rlang,expr) importFrom(rlang,have_name) importFrom(rlang,is_atomic) importFrom(rlang,is_empty) @@ -216,6 +234,7 @@ importFrom(rlang,is_named) importFrom(rlang,quo_get_expr) importFrom(rlang,sym) importFrom(rlang,syms) +importFrom(rlang,warn) importFrom(stats,median) importFrom(stats,na.omit) importFrom(stats,rnorm) diff --git a/R/AAAA.R b/R/AAAA.R index 2dac9102..97925be8 100644 --- a/R/AAAA.R +++ b/R/AAAA.R @@ -11,8 +11,8 @@ #' @importFrom tidyr complete #' @importFrom forcats fct_inorder #' @importFrom rlang sym syms quo_get_expr as_string := .data .env is_empty -#' @importFrom rlang enquo enquos is_named is_atomic have_name -#' @importFrom rlang env_clone +#' @importFrom rlang enquo enquos expr is_named is_atomic have_name +#' @importFrom rlang env_clone abort warn #' @importFrom glue glue #' @importFrom tibble tibble as_tibble is_tibble #' @importFrom stats median rnorm sd na.omit setNames update diff --git a/R/glossary.R b/R/glossary.R new file mode 100644 index 00000000..76fc6182 --- /dev/null +++ b/R/glossary.R @@ -0,0 +1,398 @@ +#' Read and parse a glossary file +#' +#' Files may be formatted as TeX glossary file or +#' in yaml format suitable for reading using +#' [yaml_as_df()] (see details). +#' +#' @param file path to the tex or yaml glossary file. +#' @param format the glossary file format; could be either `"tex"` or `"yaml"` +#' and will be inferred by default from the extension of `file`. +#' +#' @return +#' A named list of glossary definitions with class `tex_glossary` and +#' `glossary`. The list names are acronym entry labels (i.e., what you would +#' pass to `\gls{}` when writing a tex document). +#' +#' +#' @details +#' For the tex format, the glossary file is expected to contain acronym +#' entries with the form +#' +#' ``` +#' \newacronym{label}{abbreviation}{definition} +#' ``` +#' +#' or +#' +#' ``` +#' \newacronym[options]{label}{abbreviation}{definition} +#' ``` +#' +#' For the yaml format, the glossary file is expected to be able to be +#' read with [yaml_as_df()]; the outer level should be the `label` and +#' columns should be provided with the names `def` (definition) and +#' `abb` (abbreviation). See **Examples**. +#' +#' +#' +#' @examples +#' texfile <- system.file("glo", "glossary.tex", package = "pmtables") +#' +#' x <- read_glossary(texfile) +#' +#' head(x) +#' +#' x$WT +#' +#' x$SC +#' +#' yamfile <- system.file("glo", "glossary.yaml", package = "pmtables") +#' +#' y <- read_glossary(yamfile) +#' head(y) +#' +#' # Format of the tex glossary file +#' head(readLines(texfile)) +#' +#' # Format of the yaml glossary file +#' head(readLines(yamfile)) +#' +#' yaml_as_df(yamfile) +#' +#' +#' @seealso [as_glossary()] +#' @export +read_glossary <- function(file, format = guess_glo_fmt(file)) { + if(!file.exists(file)) { + abort(glue("Glossary file {file} does not exist.")) + } + if(!format %in% c("tex", "yaml", "yml")) { + abort("format should be either tex, yaml, or yml") + } + if(format == "tex") { + ans <- read_tex_glossary(file) + } else { + ans <- read_yaml_glossary(file) + } + class(ans) <- c("tex_glossary", "glossary", "list") + ans +} + +read_yaml_glossary <- function(file) { + parsed <- try(yaml_as_df(file, row_var = "label"), silent = TRUE) + if(inherits(parsed, "try-error")) { + abort( + c("Failed to parse glossary file; see ?yaml_as_df() for help formatting this file.", + as.character(parsed) + ) + ) + } + if(!all(c("label", "abb", "def") %in% names(parsed))) { + abort("yaml/yml glossary format requires names label, abb, and def.") + } + labels <- parsed[["label"]] + parsed <- select(parsed, "abb", "def") + data <- split(parsed, seq(nrow(parsed))) + data <- lapply(data, unlist) + data <- lapply(data, as_glossary_entry) + data <- lapply(data, setNames, c("abbreviation", "definition")) + names(data) <- labels + data +} + +read_tex_glossary <- function(file) { + txt <- readLines(file, warn = FALSE) + if(!length(txt)) abort("The glossary file was empty.") + parse_tex_glossary(txt) +} + +parse_tex_glossary <- function(txt) { + txt <- trimws(txt) + txt <- txt[grepl("^\\\\newacronym", txt)] + txt <- sub("[^\\\\]%.*", "", txt, perl = TRUE) + m <- regexec("\\{(.+)\\}\\s*\\{(.+)\\}\\s*\\{(.+)\\}.*$", txt, perl = TRUE) + parsed <- regmatches(txt, m) + parsed <- lapply(parsed, trimws) + if(!length(parsed)) { + abort("No acronym entries were found in `file`.") + } + if(!all(vapply(parsed, length, 1L)==4)) { + abort("There was a problem parsing the glossary file.") + } + label <- vapply(parsed, FUN = "[", 2L, FUN.VALUE = "a") + data <- lapply(parsed, FUN = "[", c(3L, 4L)) + data <- lapply(data, as_glossary_entry) + data <- lapply(data, setNames, c("abbreviation", "definition")) + names(data) <- label + data +} + +#' Update the abbreviation for a glossary entry +#' +#' @param x a glossary object. +#' @param ... `