Explorateur d'ontologies i2b2 HEGP
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
6.4KB

  1. library(data.tree)
  2. library(shiny)
  3. library(shinyTree)
  4. library(shinyjs)
  5. ## - 'yes' branch of 'if' covers everything which should not be changed
  6. ## e.g. "/images/ball.jpg" or "fa fa-file
  7. ## - 'no' branch of 'if' covers all cases which need to be changed:
  8. ## use regex (str_match) to capture groups:
  9. ## * group 1 is either 'glyphicon', 'fa' or 'NA' (if not present)
  10. ## * group 2 is the rest wihtout a potential dash '-'
  11. ## * if group 1 is empty set it to 'fa'
  12. ## * paste the pieces together
  13. fixIconName <- function(icon)
  14. {
  15. res <- ifelse(grepl("[/\\]|(glyphicon|fa) \\1-", icon),
  16. icon,
  17. {
  18. parts <- str_match(icon, "(glyphicon|fa)*-*(\\S+)")
  19. parts[, 2] <- ifelse(is.na(parts[, 2]), "fa", parts[, 2])
  20. paste(parts[, 2], paste(parts[, 2], parts[, 3], sep = "-"))
  21. })
  22. ## if NULL was given as parameter res will be length zero
  23. if (!length(res)) {
  24. NULL
  25. } else {
  26. res
  27. }
  28. }
  29. treeToJSON <- function(tree,
  30. keepRoot = FALSE,
  31. topLevelSlots = c("default", "all"),
  32. createNewId = TRUE,
  33. pretty = FALSE)
  34. {
  35. ## match against "default"/"all", if this returns an error we take topLevelSlots as is
  36. ## i.e. a vector of names to keep
  37. if (!requireNamespace("data.tree", quietly = TRUE))
  38. {
  39. msg <- paste("library", sQuote("data.tree"), "cannot be loaded. Try to run",
  40. sQuote("install.packages(\"data.tree\")"))
  41. stop(msg, domain = NA)
  42. }
  43. nodesToKeep <- list(default = c("id", "text", "icon", "state", "li_attr", "a_attr", "type"),
  44. all = NULL)
  45. topLevelSlots <- tryCatch(nodesToKeep[[match.arg(topLevelSlots)]],
  46. error = function(e) topLevelSlots)
  47. node_to_list <- function(node, node_name = NULL)
  48. {
  49. fields <- mget(node$attributes, node)
  50. NOK <- sapply(fields, function(slot) !is.atomic(slot) && !is.list(slot))
  51. if (any(NOK))
  52. {
  53. msg <- sprintf(ngettext(length(which(NOK)),
  54. "unsupported slot of type %s at position %s",
  55. "unsupported slots of types %s at positions %s"),
  56. paste0(dQuote(sapply(fields[NOK], typeof)),
  57. collapse = ", "),
  58. paste0(sQuote(names(fields)[NOK]),
  59. collapse = ", "))
  60. warning(msg, domain = NA)
  61. fields[NOK] <- NULL
  62. }
  63. if (is.null(fields$text))
  64. {
  65. fields$text <- if(!is.null(fields$name)) fields$name else node_name
  66. }
  67. fields$icon <- fixIconName(fields$icon)
  68. if (!is.null(fields$state))
  69. {
  70. valid_states <- c("opened", "disabled", "selected", "loaded")
  71. states_template <- stats::setNames(rep(list(FALSE), length(valid_states)), valid_states)
  72. NOK <- !names(fields$state) %in% valid_states
  73. if (any(NOK))
  74. {
  75. msg <- sprintf(ngettext(length(which(NOK)), "invalid state %s", "invalid states %s"),
  76. paste0(dQuote(names(fields$state)[NOK]), collapse = ", "))
  77. warning(msg, domain = NA)
  78. }
  79. states_template[names(fields$state[!NOK])] <- fields$state[!NOK]
  80. fields$state <- states_template
  81. }
  82. if (is.null(topLevelSlots))
  83. {
  84. slots_to_move <- character(0)
  85. } else {
  86. slots_to_move <- names(fields)[!names(fields) %in% topLevelSlots]
  87. }
  88. data_slot <- fields[slots_to_move]
  89. if (length(data_slot))
  90. {
  91. fields$data <- data_slot
  92. fields[slots_to_move] <- NULL
  93. }
  94. if (!is.null(node$children))
  95. {
  96. ## purrr::imap would make code cleaner but did not want to add another dependency
  97. ## unname needed to create an JSON array as opposed to an JSON object
  98. fields$children <- unname(lapply(names(node$children),
  99. function(i) node_to_list(node$children[[i]],
  100. i)))
  101. }
  102. fields
  103. }
  104. ## clone tree as we do not want to alter the original tree
  105. tree <- data.tree::Clone(tree)
  106. nodes <- data.tree::Traverse(tree, filterFun = data.tree::isNotRoot)
  107. old_ids <- data.tree::Get(nodes, "id")
  108. if (createNewId)
  109. {
  110. if (any(!is.na(old_ids)))
  111. {
  112. warning(paste("slot", dQuote("id"), "will be stored in", dQuote("id.orig")),
  113. domain = NA)
  114. data.tree::Set(nodes, id.orig = old_ids)
  115. }
  116. new_ids <- seq_along(nodes)
  117. } else {
  118. if (any(is.na(old_ids)) || any(duplicated(old_ids)))
  119. {
  120. warning(paste("old ids are invalid (duplicated values or NA),", "creating new ids"),
  121. domain = NA)
  122. new_ids <- seq_along(nodes)
  123. } else {
  124. new_ids <- old_ids
  125. }
  126. }
  127. data.tree::Set(nodes, id = new_ids)
  128. treeList <- node_to_list(tree)
  129. if (!keepRoot)
  130. {
  131. ## to prune off the root node return the first children list
  132. treeList <- treeList$children
  133. }
  134. ## use as.character b/c updateTree needs an unparsed JSON string, as
  135. ## the parsing is done in shinyTree.js
  136. as.character(jsonlite::toJSON(treeList,
  137. auto_unbox = TRUE,
  138. pretty = pretty))
  139. }
  140. # returns a data.tree
  141. df_to_tree <- function(df)
  142. {
  143. df %>% as.Node(pathName = "C_FULLNAME2", pathDelimiter = "\\")
  144. }
  145. # returns a nested list with attributes
  146. # structure("a" = list("aa" = list(), "ab" = list(), "ac" = list()), attr1 = F, attr2 = "rr")
  147. df_to_treelist <- function(df)
  148. {
  149. f <- function(l)
  150. {
  151. s <- structure(list())
  152. attributes(s) <- split(unname(l), names(l))
  153. s
  154. }
  155. a <- apply(df, 1, f)
  156. names(a) <- lapply(strsplit(df$C_FULLNAME2, "\\", T), function(s){tail(s, n=1)})
  157. a
  158. }
  159. get_path <- function(node, ancestors)
  160. {
  161. paste("\\i2b2", paste(c(ancestors, node), collapse = "\\"), "", sep = "\\")
  162. }
  163. get_line <- function(df, node_path)
  164. {
  165. df %>% filter(C_FULLNAME2 == node_path)
  166. }
  167. # request children with a fullname that contains the path of the current node and with a level 1 above
  168. request_children <- function(metadata, node_path, node_level)
  169. {
  170. metadata %>%
  171. filter(C_HLEVEL == node_level + 1,
  172. str_detect(C_FULLNAME2, fixed(node_path)))
  173. }
  174. search_in_df <- function(metadata, search_term, search_on="C_NAME", use_regex = F)
  175. {
  176. if (use_regex)
  177. {
  178. metadata %>% filter(str_detect(!!as.symbol(search_on), regex(search_term, ignore_case=T)))
  179. } else {
  180. metadata %>% filter(str_detect(!!as.symbol(search_on), fixed(search_term, ignore_case=T)))
  181. }
  182. }