R/TKCat.R

Defines functions .get_tkcat_fields .get_tkcat_tables .build_etkc_server.TKCat .build_etkc_ui.TKCat explore_MDBs.TKCat collection_members.TKCat get_MDB.TKCat search_MDB_fields.TKCat search_MDB_tables.TKCat list_MDBs.TKCat c.TKCat rename.TKCat print.TKCat format.TKCat is.TKCat scan_fileMDBs TKCat

Documented in collection_members.TKCat c.TKCat explore_MDBs.TKCat get_MDB.TKCat is.TKCat list_MDBs.TKCat rename.TKCat scan_fileMDBs search_MDB_fields.TKCat search_MDB_tables.TKCat TKCat

###############################################################################@
#' TKCat: a catalog of [MDB]
#'
#' @param list a list of [MDB] objects
#' @param ... [MDB] objects used if list is NULL
#'
#' @return a TKCat object
#' 
#' @seealso [scan_fileMDBs]
#' 
#' @export
#'
TKCat <- function(..., list=NULL){
   if(is.null(list)){
      toRet <- list(...)
   }else{
      toRet <- list
   }
   if(any(!unlist(lapply(toRet, is.MDB)))){
      stop("All provided objects should be MDB objects")
   }
   dbnames <- unlist(lapply(toRet, function(x) db_info(x)$name))
   if(any(duplicated(dbnames))){
      stop("MDB objects cannot have the same names")
   }
   names(toRet) <- dbnames
   class(toRet) <- c("TKCat", class(toRet))
   attr(toRet, "tables") <- .get_tkcat_tables(toRet)
   attr(toRet, "fields") <- .get_tkcat_fields(toRet)
   return(toRet)
}


###############################################################################@
#' Scan a catalog of [fileMDB]
#'
#' @param path directory from which all the [fileMDB] should be read
#' @param subdirs the sub directories (relative to path) to take into account.
#' If NULL (default) all the sub directories are considered.
#' @param check logical: if TRUE (default) the data are confronted to the
#' data model
#' @param n_max maximum number of records to read
#' for checks purpose (default: 10). See also [ReDaMoR::confront_data()].
#'
#' @return a TKCat object
#' 
#' @seealso [read_fileMDB]
#' 
#' @export
#'
scan_fileMDBs <- function(path, subdirs=NULL, check=TRUE, n_max=10){
   if(is.null(subdirs)){
      files <- list.files(path=path, full.names=TRUE)
   }else{
      files <- file.path(path, subdirs)
   }
   toRet <- list()
   for(f in files){
      toAdd <- suppressWarnings(try(read_fileMDB(
         path=f, check=check, n_max=n_max
      ), silent=TRUE))
      if(!inherits(toAdd, "try-error")){
         toRet <- c(toRet, list(toAdd))
      }else{
         warning(paste(basename(f), as.character(toAdd), sep=": "))
      }
   }
   return(TKCat(list=toRet))
}


###############################################################################@
#' Check the object is  a [TKCat] object
#' 
#' @param x any object
#' 
#' @return A single logical: TRUE if x is a [TKCat] object
#' 
#' @export
#'
is.TKCat <- function(x){
   inherits(x, "TKCat")
}


###############################################################################@
#' @export
#'
format.TKCat <- function(x, ...){
   toRet <- sprintf("TKCat gathering %s MDB objects", length(x))
   return(toRet)
}


###############################################################################@
#' @export
#'
print.TKCat <- function(x, ...){
   cat(format(x, ...), "\n")
   invisible()
}

###############################################################################@
#' 
#' @param x a [TKCat] object
#' @param value new [MDB] names
#' 
#' @rdname TKCat
#' 
#' @export
#'
'names<-.TKCat' <- function(x, value){
   stopifnot(
      is.character(value),
      !any(is.na(value)),
      !any(duplicated(value)),
      length(value)==length(x)
   )
   dmt <- attr(x, "tables")
   dmf <- attr(x, "fields")
   x <- unclass(x)
   for(i in 1:length(x)){
      dbi <- db_info(x[[i]])
      dmt[which(dmt$resource==dbi$name), "resource"] <- value[i]
      dmf[which(dmf$resource==dbi$name), "resource"] <- value[i]
      dbi$name <- value[i]
      db_info(x[[i]]) <- dbi
   }
   names(x) <- value
   class(x) <- c("TKCat", class(x))
   attr(x, "tables") <- dmt
   attr(x, "fields") <- dmf
   return(x)
}

###############################################################################@
#' Rename a [TKCat] object
#'
#' @param .data a [TKCat] object
#' @param ... Use new_name = old_name to rename selected [MDB]
#' 
#' @rdname TKCat
#' 
#' @export
#' 
rename.TKCat <- function(.data, ...){
   loc <- tidyselect::eval_rename(rlang::expr(c(...)), .data)
   names <- names(.data)
   names[loc] <- names(loc)
   magrittr::set_names(.data, names)
}

###############################################################################@
#' 
#' @param x a [TKCat] object
#' @param i index or names of the MDB to take
#'
#' @rdname TKCat
#' 
#' @export
#'
'[.TKCat' <- function(x, i){
   dmt <- attr(x, "tables")
   dmf <- attr(x, "fields")
   x <- unclass(x)[i]
   class(x) <- c("TKCat", class(x))
   attr(x, "tables") <- dmt %>% dplyr::filter(.data$resource %in% names(x))
   attr(x, "fields") <- dmf %>% dplyr::filter(.data$resource %in% names(x))
   return(x)
}

###############################################################################@
#' @export
#'
'[<-.TKCat' <- function(x, i, value){
   stop("'[<-' is not supported for TKCat")
}

###############################################################################@
#' @export
#'
'[[<-.TKCat' <- function(x, i, value){
   stop("'[[<-' is not supported for TKCat")
}

###############################################################################@
#' @export
#'
'$<-.TKCat' <- function(x, i, value){
   stop("'$<-' is not supported for TKCat")
}


###############################################################################@
#'
#' @param ... [TKCat] objects
#'
#' @rdname TKCat
#' 
#' @export
#'
c.TKCat <- function(...){
   alltkcat <- list(...)
   if(length(alltkcat)==0){
      stop("At least one TKCat should be provided as an input")
   }
   if(any(!unlist(lapply(alltkcat, is.TKCat)))){
      stop("All arguments should be TKCat objects")
   }
   allnames <- unlist(lapply(alltkcat, names))
   if(any(duplicated(allnames))){
      stop("Same names cannot be used in the different TKCat objects")
   }
   dmt <- do.call(dplyr::bind_rows, lapply(
      alltkcat,
      attr, which="tables"
   ))
   dmf <- do.call(dplyr::bind_rows, lapply(
      alltkcat,
      attr, which="fields"
   ))
   toRet <- do.call(c, lapply(alltkcat, unclass))
   class(toRet) <- c("TKCat", class(toRet))
   attr(toRet, "tables") <- dmt
   attr(toRet, "fields") <- dmf
   return(toRet)
}


###############################################################################@
#' 
#' @rdname list_MDBs
#' @method list_MDBs TKCat
#' 
#' @export
#'
list_MDBs.TKCat <- function(x, withInfo=TRUE){
   if(!withInfo){
      return(names(x))
   }
   return(do.call(dplyr::bind_rows, lapply(
      x,
      function(y) dplyr::as_tibble(db_info(y))
   )))
}


###############################################################################@
#' 
#' @rdname search_MDB_tables
#' @method search_MDB_tables TKCat
#' 
#' @export
#'
search_MDB_tables.TKCat <- function(x, searchTerm){
   dmt <- attr(x, "tables")
   toTake <- unique(c(
      grep(searchTerm, dmt$name, ignore.case=TRUE),
      grep(searchTerm, dmt$comment, ignore.case=TRUE)
   ))
   toRet <- dmt %>% dplyr::slice(c(0, toTake))
   return(toRet)
}


###############################################################################@
#' 
#' @rdname search_MDB_fields
#' @method search_MDB_fields TKCat
#' 
#' @export
#'
search_MDB_fields.TKCat <- function(x, searchTerm){
   dmf <- attr(x, "fields")
   toTake <- unique(c(
      grep(searchTerm, dmf$name, ignore.case=TRUE),
      grep(searchTerm, dmf$comment, ignore.case=TRUE)
   ))
   toRet <- dmf %>% dplyr::slice(c(0, toTake))
   return(toRet)
}



###############################################################################@
#' 
#' @rdname get_MDB
#' @method get_MDB TKCat
#' 
#' @export
#'
get_MDB.TKCat <- function(x, dbName, ...){
   stopifnot(dbName %in% names(x))
   return(x[[dbName]])
}

###############################################################################@
#' 
#' @rdname collection_members
#' @method collection_members TKCat
#' 
#' @export
#'
collection_members.TKCat <- function(
   x,
   ...
){
   return(do.call(dplyr::bind_rows, lapply(
      x,
      function(y){
         cm <- collection_members(y)
         if(is.null(cm)){
            return(NULL)
         }
         cm %>% 
            dplyr::select("resource", "collection", "table") %>% 
            dplyr::distinct()
      }
   )))
}


###############################################################################@
#### SHINY EXPLORER ####
###############################################################################@


###############################################################################@
#'
#' @param subSetSize the maximum number of records to show
#' @param download a logical indicating if data can be downloaded
#' (default: FALSE). If TRUE a temporary directory is created and made
#' available for shiny.
#' @param workers number of available workers when download is available
#' (default: 4)
#' @param skinColors one color for the application skin.
#' Working values: "blue", "black", "purple", "green", "red", "yellow".
#' @param title A title for the application. If NULL (default):
#' the chTKCat instance name
#' @param logoDiv a [shiny::div] object with a logo to display in side bar.
#' The default is the TKCat hex sticker with a link to TKCat github repository.
#' @param rDirs a named character vector with resource path
#' for [shiny::addResourcePath]
#' @param tabTitle a title to display in tab (default: "chTKCat")
#' @param tabIcon a path to an image
#' (in available resource paths: "www", "doc" or in rDirs) to use as a tab icon.
#' 
#' @rdname explore_MDBs
#' @method explore_MDBs TKCat
#' 
#' @export
#'
explore_MDBs.TKCat <- function(
   x,
   subSetSize=100,
   download=FALSE,
   workers=4,
   title=NULL,
   skinColors="green",
   logoDiv=TKCAT_LOGO_DIV,
   rDirs=NULL,
   tabTitle="TKCat",
   tabIcon='www/TKCat-small.png',
   ...
){
   stopifnot(
      is.logical(download), length(download)==1, !is.na(download),
      is.character(skinColors), length(skinColors)>0, all(!is.na(skinColors))
   )
   skinColors <- skinColors[1]
   if(download){
      ddir <- tempfile()
      dir.create(ddir)
      oplan <- future::plan(
         future::multisession, workers=workers
      )
   }else{
      ddir <- NULL
   }
   shiny::shinyApp(
      ui=.build_etkc_ui(
         x=x, ddir=ddir, skinColors=skinColors,
         logoDiv=logoDiv, rDirs=rDirs,
         tabTitle=tabTitle, tabIcon=tabIcon,
      ),
      server=.build_etkc_server(
         x=x,
         subSetSize=subSetSize,
         ddir=ddir,
         title=title
      ),
      enableBookmarking="url",
      onStart=function(){
         shiny::onStop(function(){
            unlink(ddir, recursive=TRUE, force=TRUE)
            if(exists("oplan")){
               future::plan(oplan)
            }
         })
      }
   )
}

###############################################################################@
.build_etkc_ui.TKCat <- function(
   x, ddir=NULL, skinColors="green",
   logoDiv=TKCAT_LOGO_DIV, rDirs=NULL,
   tabTitle="TKCat",
   tabIcon='www/TKCat-small.png',
   ...
){
   
   .etkc_add_resources(ddir=ddir, rDirs=rDirs)
   
   function(req){
      shinydashboard::dashboardPage(
         title=tabTitle,
         skin=skinColors[1],
         
         ########################@
         ## Dashboard header ----
         ## Uses output$instance and output$status
         header=.etkc_sd_header(),
         
         ########################@
         ## Sidebar ----
         ## Uses uiOutput("currentUser") and uiOutput("signin")
         sidebar=.etkc_sd_sidebar(
            sysInterface=FALSE,
            manList=c(
               "TKCat user guide"="doc/TKCat-User-guide.html"
            ),
            logoDiv=logoDiv
         ),
         
         ########################@
         ## Body ----
         body=.etkc_sd_body(sysInterface=FALSE, tabIcon=tabIcon)
      )
   }
   
}


###############################################################################@
.build_etkc_server.TKCat <- function(
   x,
   subSetSize=100,
   ddir=NULL,
   title=NULL
){
   .build_etkc_server_default(
      x=x, subSetSize=subSetSize,
      ddir=ddir,
      title=title
   )
}


###############################################################################@
## Helpers ----
.get_tkcat_tables <- function(x){
   dmt <- c()
   for(n in names(x)){
      dm <- data_model(get_MDB(x, n))
      dmt <- dplyr::bind_rows(
         dmt,
         ReDaMoR::toDBM(dm)$tables %>% 
            dplyr::mutate(resource=n) %>% 
            dplyr::select("resource", "name", "comment")
      )
   }
   return(dmt)
}
.get_tkcat_fields <- function(x){
   dmf <- c()
   for(n in names(x)){
      dm <- data_model(get_MDB(x, n))
      dmf <- dplyr::bind_rows(
         dmf,
         ReDaMoR::toDBM(dm)$fields %>% 
            dplyr::mutate(resource=n) %>% 
            dplyr::select(
               "resource", "table", "name",
               "type", "nullable", "unique", "comment"
            )
      )
   }
   return(dmf)
}

Try the TKCat package in your browser

Any scripts or data that you put into this service are public.

TKCat documentation built on Feb. 16, 2023, 10:51 p.m.