insightsengineering / teal

Exploratory Web Apps for Analyzing Clinical Trial Data
https://insightsengineering.github.io/teal/
Other
171 stars 34 forks source link

[Bug]: Snapshot names collide #1131

Closed averissimo closed 6 months ago

averissimo commented 6 months ago

What happened?

How to reproduce:

This is due to the base::make.names function generating syntathically valid names for use in R.

image

Additionally, shinytest2::get_download() function does not work well with output ID names that have dots in them.

Suggestion:

#' Make Syntactically Valid Names
#'
#' Extends [base::make.names()] to allow to use underscore as a separator and
#' to add a hash to the end of the name.
#'
#' @param names (`character`) vector of names coerced to syntactically valid names.
#' This is coerced to character if necessary
#' @inherit base::make.names return
#' @keywords internal
#'
make_names <- function(names) {
  checkmate::assert_character(names)
  names <- trimws(names)
  sprintf(
    "%s-%s",
    gsub("[.]", "_", make.names(names)),
    substr(vapply(names, rlang::hash, "character"), 0L, 5L)
  )
}
chlebowa commented 6 months ago

This is a feature, not a bug. The user is encouraged to pick better names. They can add a digit themselves.

averissimo commented 6 months ago

I don't think this is robust enough to filter out bad names. A Snapshot, A.Snapshot and A-Snapshot collide while A_Snapshot and a few other exceptions don't

We could debate if having symbols produce bad names, but I'm ok with the rationale. ~However, whitespace seems problematic as it wouldn't be visible on the UI.~

make.names(c("A Snapshot", "A.Snapshot", "A-Snapshot", "A$Snapshot", "A/Snapshot", "A%Snapshot"))
#> [1] "A.Snapshot" "A.Snapshot" "A.Snapshot" "A.Snapshot" "A.Snapshot"
#> [6] "A.Snapshot"
make.names("prefix\\|!\"#$%&/()=?»«'*+`´ºª~^çÇ.-_,;:<>¨·€ß@£§½¬{[]}¸¹¡suffix")
#> [1] "prefix....................ºª..çÇ.._........ß............suffix"
make.names(c("A_Snapshot", " A Snapshot ", "   A Snapshot   "))
#> [1] "A_Snapshot"        "X.A.Snapshot."     "X...A.Snapshot..."

~Maybe we could extend this feature to filter out whitespace and ALL the symbols (trimws(gsub("\\W|_", ".", trimws(names)), whitespace = "[.]")) :smile:~

make_names <- function(names) {
  checkmate::assert_character(names)
  trimws(gsub("\\W|_", ".", make.names(trimws(names))), whitespace = "[.]")}
}
averissimo commented 6 months ago

Nevermind, whitespace is protected somewhere before make.names call.

Allowing only "_" and a few exceptions seems reasonable

chlebowa commented 6 months ago

Leading and trailing white space is removed, yes.