diff --git a/.Rbuildignore b/.Rbuildignore new file mode 100644 index 0000000..12948cf --- /dev/null +++ b/.Rbuildignore @@ -0,0 +1,5 @@ +^renv$ +^renv\.lock$ +^.*\.Rproj$ +^\.Rproj\.user$ +^\.github$ diff --git a/.github/.gitignore b/.github/.gitignore new file mode 100644 index 0000000..2d19fc7 --- /dev/null +++ b/.github/.gitignore @@ -0,0 +1 @@ +*.html diff --git a/.github/actions/renv-plus/LICENSE b/.github/actions/renv-plus/LICENSE new file mode 100644 index 0000000..71d3725 --- /dev/null +++ b/.github/actions/renv-plus/LICENSE @@ -0,0 +1,25 @@ +The MIT License (MIT) +===================== + +Copyright © 2021 RStudio and others. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/.github/actions/renv-plus/README.md b/.github/actions/renv-plus/README.md new file mode 100644 index 0000000..f1ac2c3 --- /dev/null +++ b/.github/actions/renv-plus/README.md @@ -0,0 +1,32 @@ +# setup-renv + +[![RStudio community](https://img.shields.io/badge/community-github--actions-blue?style=social&logo=rstudio&logoColor=75AADB)](https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions) + +This action installs dependencies for the current R environment based on the renv lockfile in the repository by: + +- Installing [renv](https://rstudio.github.io/renv/articles/renv.html) +- Setting up a dependency cache using [actions/cache](https://github.com/actions/cache). + +# Usage + +Inputs available + +- `cache-version` - default `1`. If you need to invalidate the existing cache pass any other number and a new cache will be used. + +Basic: +```yaml +steps: +- uses: actions/checkout@v2 +- uses: r-lib/actions/setup-r@v2 +- uses: r-lib/actions/setup-renv@v2 + with: + cache-version: 2 +``` + +# License + +The scripts and documentation in this project are released under the [MIT License](LICENSE) + +# Contributions + +Contributions are welcome! diff --git a/.github/actions/renv-plus/action.yaml b/.github/actions/renv-plus/action.yaml new file mode 100644 index 0000000..5dd9531 --- /dev/null +++ b/.github/actions/renv-plus/action.yaml @@ -0,0 +1,48 @@ +name: 'setup-renv' +description: 'Action to setup renv and install R dependencies in the lockfile' +author: 'Jim Hester' +inputs: + restore-args: + description: 'Arguments passed to renv::restore()' + default: '' + cache-version: + description: 'The version of the cache, change this from the default (1) to start over with a fresh cache' + required: true + default: 1 +runs: + using: "composite" + steps: + - name: Set RENV_PATHS_ROOT + shell: bash + run: | + echo "RENV_PATHS_ROOT=${{ runner.temp }}/renv" >> $GITHUB_ENV + - name: Install and activate renv + run: | + install.packages("renv") + renv::activate() + shell: Rscript {0} + + - name: Get R and OS version + id: get-version + run: | + cat("##[set-output name=os-version;]", sessionInfo()$running, "\n", sep = "") + cat("##[set-output name=r-version;]", R.Version()$version.string, sep = "") + shell: Rscript {0} + + - name: Restore Renv package cache + uses: actions/cache@v2 + with: + path: ${{ env.RENV_PATHS_ROOT }} + key: ${{ steps.get-version.outputs.os-version }}-${{ steps.get-version.outputs.r-version }}-${{inputs.cache-version }}-${{ hashFiles('renv.lock') }} + restore-keys: ${{ steps.get-version.outputs.os-version }}-${{ steps.get-version.outputs.r-version }}-${{inputs.cache-version }}- + + - name: Install renv dependencies + run: renv::restore(${{ inputs.restore-args }}) + shell: Rscript {0} + + - name: Don't use tar 1.30 from Rtools35 to store the cache + shell: bash + run: | + if command -v /c/Rtools/bin/tar && /c/Rtools/bin/tar --version | grep -q 'tar (GNU tar) 1.30' + then echo 'C:/Program Files/Git/usr/bin' >> $GITHUB_PATH + fi \ No newline at end of file diff --git a/.github/workflows/test-app.yaml b/.github/workflows/test-app.yaml new file mode 100644 index 0000000..d68a9d8 --- /dev/null +++ b/.github/workflows/test-app.yaml @@ -0,0 +1,57 @@ + + +# Workflow derived from https://github.com/rstudio/shinytest2/tree/main/actions/test-app/example-test-app-description.yaml +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help +on: + push: + branches: [main, master] + pull_request: + branches: [main, master] + +name: Test app w/ DESCRIPTION + +jobs: + test-app: + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + + strategy: + fail-fast: false + matrix: + config: + - {os: ubuntu-latest, r: release} + + env: + GITHUB_PAT: ${{ secrets.EXTERNAL_GITHUB_TOKEN }} + GITLAB_PAT: ${{ secrets.GITLAB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + + steps: + - uses: actions/checkout@v2 + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true + + - name: Install system dependencies + run: | + sudo apt-get install libcurl4-openssl-dev libv8-dev + + - uses: ./.github/actions/renv-plus + with: + cache-version: 2 + restore-args: 'exclude = "mapDataAccess"' + + - name: Install extra dependencies + run: | + install.packages("shinytest2") + shell: Rscript {0} + + - uses: rstudio/shinytest2/actions/test-app@v1 + with: + path: "." \ No newline at end of file diff --git a/FREDA.Rproj b/FREDA.Rproj index 8e3c2eb..21a4da0 100644 --- a/FREDA.Rproj +++ b/FREDA.Rproj @@ -11,3 +11,7 @@ Encoding: UTF-8 RnwWeave: Sweave LaTeX: pdfLaTeX + +BuildType: Package +PackageUseDevtools: Yes +PackageInstallArgs: --no-multiarch --with-keep.source diff --git a/Observers/groups_observers.R b/Observers/groups_observers.R index ca3a3b9..8e7edcf 100644 --- a/Observers/groups_observers.R +++ b/Observers/groups_observers.R @@ -8,7 +8,8 @@ observeEvent(input$add_group, { updateTextInput(session, "group_name", value = "") updatePickerInput(session, "group_samples", selected = setdiff(sample_names(), c(unlist(revals$groups_list), input$group_samples))) - + + exportTestValues(groups_list = revals$groups_list) }) # shinyjs observer which disables input if selection is not valid diff --git a/Observers/preprocess_observers.R b/Observers/preprocess_observers.R index 79d9e22..5e3dcbe 100644 --- a/Observers/preprocess_observers.R +++ b/Observers/preprocess_observers.R @@ -53,7 +53,6 @@ observeEvent(input$preprocess_click, { for(i in 1:length(valence_list)){ V <- valence_list[[i]] for(name in names(V)){ - print(V[[name]]) valence_df[i,name] <- V[[name]] } } @@ -93,50 +92,41 @@ observeEvent(input$preprocess_click, { revals$warningmessage_preprocess$makeobject_error <<- sprintf("

%s

", msg) }) - if(!exists('msg')) revals$uploaded_data <- temp + if(!exists('msg')) { + revals$uploaded_data <- temp + exportTestValues(uploaded_data_processed = revals$uploaded_data) + } }) - # post mortem test object - # test_uploaded_data <<- revals$peakData2 - - if (isTRUE(getOption("shiny.testmode"))) { - exportTestValues(peakData2 = revals$peakData2) - } - }, priority = 10) # End action button event # Creates two reactive variables for continuous and categorical variables which are used to display separate tables # Note: dependent on preprocess click and the user-specified calculations observeEvent(input$preprocess_click, { - # Error handling: revals$uploaded_data must have a non-NULL Kendrick Mass column name - #req(!is.null(attr(revals$uploaded_data, 'cnames')$kmass_cname)) req(input$tests) - # Get csv file of all possible calculation column names - possible_calc_cnames <- read_csv("calculation_variables.csv") %>% as.data.frame(stringsAsFactors = FALSE) - # Get column names from revals$uploaded_data's e_meta actual_cnames <- colnames(revals$uploaded_data$e_meta) # Find all columns with names that match names for calculated columns - v_index <- which(possible_calc_cnames[,1] %in% actual_cnames) + v_index <- which(names(PREPROCESS_CALC_VARS) %in% actual_cnames) # Save calculation column names from above and their display names - intersect <- possible_calc_cnames[v_index,] + present_vars <- PREPROCESS_CALC_VARS[v_index] # get numeric columns numeric_cols <- revals$uploaded_data$e_meta %>% - dplyr::select(which(sapply(.[intersect[,1]], is.numeric))) %>% + dplyr::select(which(sapply(.[,names(present_vars)], is.numeric))) %>% names() # get categorical columns categorical_cols <- revals$uploaded_data$e_meta %>% - dplyr::select(which(!sapply(.[intersect[,1]], is.numeric))) %>% + dplyr::select(which(!sapply(.[names(present_vars)], is.numeric))) %>% names() #set reactive variables for observers - revals$numeric_cols <- intersect %>% filter(ColumnName %in% numeric_cols) - revals$categorical_cols <- intersect %>% filter(ColumnName %in% categorical_cols) + revals$numeric_cols <- present_vars[names(present_vars) %in% numeric_cols] + revals$categorical_cols <- present_vars[names(present_vars) %in% categorical_cols] }) @@ -164,18 +154,24 @@ observeEvent(input$preprocess_dismiss,{ req(c(revals$numeric_cols, revals$categorical_cols)) - if(isTRUE(nrow(revals$numeric_cols) > 0)){ - columns <- summaryPreprocess(isolate(revals$uploaded_data), revals$numeric_cols) %>% colnames() + if(isTRUE(length(revals$numeric_cols) > 0)){ + tmp_num_table <- summaryPreprocess(isolate(revals$uploaded_data), revals$numeric_cols) - revals$preprocess_tables$numeric <- summaryPreprocess(isolate(revals$uploaded_data), revals$numeric_cols) %>% + revals$preprocess_tables$numeric <- tmp_num_table %>% datatable(options = list(dom = "t", pageLength = nrow(.))) %>% - formatRound(columns, digits = 2) + formatRound(colnames(tmp_num_table), digits = 2) + + # __SHINYTEST__ + exportTestValues(numeric_table = tmp_num_table) } - if(isTRUE(nrow(revals$categorical_cols) > 0)){ + if(isTRUE(length(revals$categorical_cols) > 0)){ revals$preprocess_tables$categorical <- summaryPreprocess(revals$uploaded_data, revals$categorical_cols, categorical = TRUE) + + # __SHINYTEST__ + exportTestValues(categorical_table = revals$preprocess_tables$categorical) } - + }) # For numeric columns: diff --git a/Observers/startup_observers.R b/Observers/startup_observers.R index 0e5474e..40ab134 100644 --- a/Observers/startup_observers.R +++ b/Observers/startup_observers.R @@ -5,6 +5,7 @@ observe({ # establish minio connection if we are pulling cloud resources if(any(names(query) %in% VALID_MINIO_HEADER_PARAMS)) { + library(mapDataAccess) minio_con <<- mapDataAccess::map_data_connection("./cfg/minio_config.yml") } diff --git a/Observers/upload_observers.R b/Observers/upload_observers.R index 766b5ed..fe2fcc7 100644 --- a/Observers/upload_observers.R +++ b/Observers/upload_observers.R @@ -158,6 +158,8 @@ observeEvent(input$upload_click, { shinyjs::show('ok_idcols') revals$uploaded_data <- res + + exportTestValues(uploaded_data = res) } }) # End peakData creation diff --git a/README.md b/README.md index 3e8fcde..5a7e2c7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # FREDA + +[![tests](https://github.com/EMSL-computing/FREDA/actions/workflows/test-app.yaml/badge.svg)](https://github.com/EMSL-computing/FREDA/actions/workflows/test-app.yaml) + *** diff --git a/global.R b/global.R index 8f99a9d..ff0e260 100644 --- a/global.R +++ b/global.R @@ -18,7 +18,6 @@ library(readr) library(plotly) library(DT) library(shinycssloaders) -library(mapDataAccess) # uncomment either library() or load_all() if you need to load kegg library(KeggData) @@ -36,7 +35,11 @@ kendrick_opts_info <- 'The base compound(s) used to calculate the Kendrick Mass. dt_checkmark <- '' dt_minus <- '' -ttip_text = list("plot_save"="Save the last created plot", "plot_review"="Review saved plots", "page_help"="How do I use this page?") +ttip_text = list( + "plot_save"="Save the last created plot", + "plot_review"="Review saved plots", + "page_help"="How do I use this page?" +) #------ Download Example Data ---------# example_edata <- read_csv('Data/example12T_edata.csv') %>% as.data.frame(stringsAsFactors = FALSE) @@ -46,6 +49,37 @@ calc_vars <- read_csv('calculation_variables.csv') %>% as.data.frame(stringsAsFa # determines when 'large data' options are triggered max_cells <- 2000000 +PREPROCESS_CALC_VARS <- list( + "ColumnName" = "DisplayName", + "OtoC_ratio" = "O:C Ratio", + "HtoC_ratio" = "H:C Ratio", + "NtoC_ratio" = "N:C Ratio", + "PtoC_ratio" = "P:C Ratio", + "NtoP_ratio" = "N:P Ratio", + "NOSC" = "NOSC", + "GFE" = "Gibbs Free Energy", + "AI" = "Aromaticity", + "AI_Mod" = "Modified Aromaticity", + "DBE" = "DBE", + "DBE_O" = "DBE - O", + "DBE_AI" = "DBE - AI", + "ElComposition" = "Elemental Composition", + "bs1_class" = "Compound Classes Using bs1", + "bs2_class" = "Compound Classes Using bs2", + "bs3_class" = "Compound Classes Using bs3" +) + +BASE_COMPS <- c("CH2", "CO2", "H2", "H2O", "CHO") + +.extra_kmass <- lapply(BASE_COMPS, function(x) sprintf("Kendrick Mass (Base %s)", x)) +names(.extra_kmass) <- paste0("kmass.", BASE_COMPS) + +.extra_kdefect <- lapply(BASE_COMPS, function(x) sprintf("Kendrick Defect (Base %s)", x)) +names(.extra_kdefect) <- paste0("kdefect.", BASE_COMPS) + +PREPROCESS_CALC_VARS <- c(PREPROCESS_CALC_VARS, .extra_kmass, .extra_kdefect) + +# Info text for things such as warnings info_text = list( COREMS_UPLOAD_ERROR = "There was an error retrieving your Core-MS files: %s", COREMS_UPLOAD_NOSAMPS = "No files found in the cloud location specified by 'corems-prefix' in the URL.", diff --git a/helper_functions/summaryPreprocess.R b/helper_functions/summaryPreprocess.R index 7794cbe..f67d0fd 100644 --- a/helper_functions/summaryPreprocess.R +++ b/helper_functions/summaryPreprocess.R @@ -17,7 +17,7 @@ summaryPreprocess <- function(ftmsRobject, testsSelected, categorical = FALSE, s if(categorical){ #Apply to each column name - lapply(testsSelected[,1], function(colname){ + lapply(names(testsSelected), function(colname){ # split any values in the column that have 2 categories split_col <- strsplit(ftmsRobject$e_meta[,colname], split_chars) %>% unlist() @@ -34,7 +34,7 @@ summaryPreprocess <- function(ftmsRobject, testsSelected, categorical = FALSE, s {`colnames<-`(., cats)} df[1,] <- tab_na # store counts - rownames(df) <- testsSelected %>% filter(ColumnName == colname) %>% pluck(2) # set rowname for display purposes + rownames(df) <- testsSelected[[colname]] # set rowname for display purposes return(df) }) @@ -44,7 +44,7 @@ summaryPreprocess <- function(ftmsRobject, testsSelected, categorical = FALSE, s else{ # Set up rownames for use in table - rowNames <- testsSelected[,2] + rowNames <- testsSelected rowNum <- length(rowNames) # Set up data frame (finally!) @@ -56,7 +56,7 @@ summaryPreprocess <- function(ftmsRobject, testsSelected, categorical = FALSE, s row.names(summaryTable) <- rowNames # Call summary and extract info - allCols <- ftmsRobject$e_meta %>% dplyr::select(testsSelected[,1]) + allCols <- ftmsRobject$e_meta %>% dplyr::select(names(testsSelected)) # NOT YET WORKING: Sapply the summary # summaryTable <- t(sapply(allCols, function(x) unname(summary(x)[c('Min.', 'Mean', 'Median', 'Max.')]))) diff --git a/renv.lock b/renv.lock index 26aba47..e151720 100644 --- a/renv.lock +++ b/renv.lock @@ -11,10 +11,13 @@ "Packages": { "DT": { "Package": "DT", - "Version": "0.19", + "Version": "0.25", "Source": "Repository", "Repository": "CRAN", - "Hash": "6df7d86466f183ab0edcd8e6050b38e1" + "Hash": "d35337fd4278be35c50d7eeeec439f68", + "Requirements": [ + "htmlwidgets" + ] }, "KeggData": { "Package": "KeggData", @@ -26,7 +29,8 @@ "RemoteUsername": "lmbramer", "RemoteRef": "new_dbmappings", "RemoteSha": "bd6bf12f1bbbd3d0543962e1729dd61271f3e3a8", - "Hash": "ce03ab7b4111dd280f973574c02d3830" + "Hash": "ce03ab7b4111dd280f973574c02d3830", + "Requirements": [] }, "MetaCycData": { "Package": "MetaCycData", @@ -38,14 +42,16 @@ "RemoteUsername": "EMSL-computing", "RemoteRef": "new_dbmappings", "RemoteSha": "e103875c4cbdd33bfacadbbc24ebb801ee4992ab", - "Hash": "b35d881fa52fefba6c57652ae399e218" + "Hash": "b35d881fa52fefba6c57652ae399e218", + "Requirements": [] }, "RColorBrewer": { "Package": "RColorBrewer", "Version": "1.1-2", "Source": "Repository", "Repository": "CRAN", - "Hash": "e031418365a7f7a766181ab5a41a5716" + "Hash": "e031418365a7f7a766181ab5a41a5716", + "Requirements": [] }, "datadr": { "Package": "datadr", @@ -57,14 +63,21 @@ "RemoteUsername": "delta-rho", "RemoteRef": "HEAD", "RemoteSha": "f3ffebd8e1c43a5d11cd3543bbe672c88f8c52f3", - "Hash": "4ddf60dd3cd5d3baf559f44d872fc74e" + "Hash": "4ddf60dd3cd5d3baf559f44d872fc74e", + "Requirements": [ + "dplyr" + ] }, "dplyr": { "Package": "dplyr", "Version": "1.0.7", "Source": "Repository", "Repository": "CRAN", - "Hash": "36f1ae62f026c8ba9f9b5c9a08c03297" + "Hash": "36f1ae62f026c8ba9f9b5c9a08c03297", + "Requirements": [ + "rlang", + "tibble" + ] }, "ftmsRanalysis": { "Package": "ftmsRanalysis", @@ -73,38 +86,69 @@ "RemoteType": "github", "RemoteHost": "api.github.com", "RemoteRepo": "ftmsRanalysis", - "RemoteUsername": "EMSL-computing", - "RemoteRef": "HEAD", - "RemoteSha": "6df6a14b2fbeb14f9578b058ba83dee2aba24b7b", - "Hash": "205ba328b26ecddbf46a14c5617385d2" + "RemoteUsername": "EMSL-Computing", + "RemoteRef": "CoreMS-compatibility-rb", + "RemoteSha": "ebc788a659a6a1445374b72025c1838d2c53e22e", + "Hash": "1a929761f2ea912e91949bd140eef7ca", + "Requirements": [ + "RColorBrewer", + "dplyr", + "ggplot2", + "plotly", + "readr", + "reshape2", + "rlang", + "scales", + "stringr", + "tibble", + "tidyr" + ] }, "ggplot2": { "Package": "ggplot2", "Version": "3.3.5", "Source": "Repository", "Repository": "CRAN", - "Hash": "d7566c471c7b17e095dd023b9ef155ad" + "Hash": "d7566c471c7b17e095dd023b9ef155ad", + "Requirements": [ + "rlang", + "scales", + "tibble", + "withr" + ] }, "htmlwidgets": { "Package": "htmlwidgets", "Version": "1.5.4", "Source": "Repository", "Repository": "CRAN", - "Hash": "76147821cd3fcd8c4b04e1ef0498e7fb" + "Hash": "76147821cd3fcd8c4b04e1ef0498e7fb", + "Requirements": [] }, "kableExtra": { "Package": "kableExtra", "Version": "1.3.4", "Source": "Repository", "Repository": "CRAN", - "Hash": "49b625e6aabe4c5f091f5850aba8ff78" + "Hash": "49b625e6aabe4c5f091f5850aba8ff78", + "Requirements": [ + "knitr", + "rmarkdown", + "scales", + "stringr", + "webshot" + ] }, "knitr": { "Package": "knitr", "Version": "1.37", "Source": "Repository", "Repository": "CRAN", - "Hash": "a4ec675eb332a33fe7b7fe26f70e1f98" + "Hash": "a4ec675eb332a33fe7b7fe26f70e1f98", + "Requirements": [ + "stringr", + "xfun" + ] }, "mapDataAccess": { "Package": "mapDataAccess", @@ -116,175 +160,249 @@ "RemoteUsername": "multiomics-analyses", "RemoteRef": "HEAD", "RemoteSha": "12e6bd1e85a406a0d28b636f8c23754d81745ac5", - "Hash": "16970b5f8a17a3d5e2dc74911b59c0b2" + "Hash": "16970b5f8a17a3d5e2dc74911b59c0b2", + "Requirements": [ + "reticulate" + ] }, "markdown": { "Package": "markdown", "Version": "1.1", "Source": "Repository", "Repository": "CRAN", - "Hash": "61e4a10781dd00d7d81dd06ca9b94e95" + "Hash": "61e4a10781dd00d7d81dd06ca9b94e95", + "Requirements": [ + "xfun" + ] }, "pander": { "Package": "pander", "Version": "0.6.4", "Source": "Repository", "Repository": "CRAN", - "Hash": "0eae8a954e0c51bd356f8c6f0e00e805" + "Hash": "0eae8a954e0c51bd356f8c6f0e00e805", + "Requirements": [] }, "plotly": { "Package": "plotly", "Version": "4.9.4.1", "Source": "Repository", "Repository": "CRAN", - "Hash": "af4b92cb3828aa30002e2f945c49c2d7" + "Hash": "af4b92cb3828aa30002e2f945c49c2d7", + "Requirements": [ + "RColorBrewer", + "dplyr", + "ggplot2", + "htmlwidgets", + "purrr", + "rlang", + "scales", + "tibble", + "tidyr" + ] }, "purrr": { "Package": "purrr", "Version": "0.3.4", "Source": "Repository", "Repository": "CRAN", - "Hash": "97def703420c8ab10d8f0e6c72101e02" + "Hash": "97def703420c8ab10d8f0e6c72101e02", + "Requirements": [ + "rlang" + ] }, "raster": { "Package": "raster", "Version": "3.4-13", "Source": "Repository", "Repository": "CRAN", - "Hash": "4543b272b756a4ff0f80370c886ef90a" + "Hash": "4543b272b756a4ff0f80370c886ef90a", + "Requirements": [ + "sp" + ] }, "readr": { "Package": "readr", "Version": "2.0.2", "Source": "Repository", "Repository": "CRAN", - "Hash": "7cb2c3ecfbc2c6786221d2c0c1f6ed68" + "Hash": "7cb2c3ecfbc2c6786221d2c0c1f6ed68", + "Requirements": [ + "rlang", + "tibble" + ] }, "renv": { "Package": "renv", - "Version": "0.14.0", + "Version": "0.15.4", "Source": "Repository", "Repository": "CRAN", - "Hash": "30e5eba91b67f7f4d75d31de14bbfbdc" + "Hash": "c1078316e1d4f70275fc1ea60c0bc431", + "Requirements": [] }, "reshape2": { "Package": "reshape2", "Version": "1.4.4", "Source": "Repository", "Repository": "CRAN", - "Hash": "bb5996d0bd962d214a11140d77589917" + "Hash": "bb5996d0bd962d214a11140d77589917", + "Requirements": [ + "stringr" + ] }, "reticulate": { "Package": "reticulate", "Version": "1.22", "Source": "Repository", "Repository": "CRAN", - "Hash": "b34a8bb69005168078d1d546a53912b2" + "Hash": "b34a8bb69005168078d1d546a53912b2", + "Requirements": [ + "withr" + ] }, "rlang": { "Package": "rlang", - "Version": "1.0.2", + "Version": "1.0.6", "Source": "Repository", "Repository": "CRAN", - "Hash": "04884d9a75d778aca22c7154b8333ec9" + "Hash": "4ed1f8336c8d52c3e750adcdc57228a7", + "Requirements": [] }, "rmarkdown": { "Package": "rmarkdown", "Version": "2.13", "Source": "Repository", "Repository": "CRAN", - "Hash": "ac78f4d2e0289d4cba73b88af567b8b1" + "Hash": "ac78f4d2e0289d4cba73b88af567b8b1", + "Requirements": [ + "knitr", + "stringr", + "xfun" + ] }, "scales": { "Package": "scales", "Version": "1.1.1", "Source": "Repository", "Repository": "CRAN", - "Hash": "6f76f71042411426ec8df6c54f34e6dd" + "Hash": "6f76f71042411426ec8df6c54f34e6dd", + "Requirements": [ + "RColorBrewer" + ] }, "shiny": { "Package": "shiny", "Version": "1.6.0", "Source": "Repository", "Repository": "CRAN", - "Hash": "6e3b6ae7fe02b5859e4bb277f218b8ae" + "Hash": "6e3b6ae7fe02b5859e4bb277f218b8ae", + "Requirements": [ + "rlang", + "withr" + ] }, "shinyBS": { "Package": "shinyBS", "Version": "0.61", "Source": "Repository", "Repository": "CRAN", - "Hash": "f895dafd39733c4a70d425f605a832e7" + "Hash": "f895dafd39733c4a70d425f605a832e7", + "Requirements": [ + "shiny" + ] }, "shinyWidgets": { "Package": "shinyWidgets", "Version": "0.6.2", "Source": "Repository", "Repository": "CRAN", - "Hash": "9bdabea3a78fd6a0768c2a319d36264e" + "Hash": "9bdabea3a78fd6a0768c2a319d36264e", + "Requirements": [ + "shiny" + ] }, "shinycssloaders": { "Package": "shinycssloaders", "Version": "1.0.0", "Source": "Repository", "Repository": "CRAN", - "Hash": "f39bb3c44a9b496723ec7e86f9a771d8" + "Hash": "f39bb3c44a9b496723ec7e86f9a771d8", + "Requirements": [ + "shiny" + ] }, "shinyjs": { "Package": "shinyjs", "Version": "2.0.0", "Source": "Repository", "Repository": "CRAN", - "Hash": "9ddfc91d4280eaa34c2103951538976f" + "Hash": "9ddfc91d4280eaa34c2103951538976f", + "Requirements": [ + "shiny" + ] }, "sp": { "Package": "sp", "Version": "1.4-5", "Source": "Repository", "Repository": "CRAN", - "Hash": "dfd843ee98246cf932823acf613b05dd" + "Hash": "dfd843ee98246cf932823acf613b05dd", + "Requirements": [] }, "stringr": { "Package": "stringr", "Version": "1.4.0", "Source": "Repository", "Repository": "CRAN", - "Hash": "0759e6b6c0957edb1311028a49a35e76" + "Hash": "0759e6b6c0957edb1311028a49a35e76", + "Requirements": [] }, "tibble": { "Package": "tibble", "Version": "3.1.5", "Source": "Repository", "Repository": "CRAN", - "Hash": "36eb05ad4cfdfeaa56f5a9b2a1311efd" + "Hash": "36eb05ad4cfdfeaa56f5a9b2a1311efd", + "Requirements": [ + "rlang" + ] }, "tidyr": { "Package": "tidyr", "Version": "1.1.4", "Source": "Repository", "Repository": "CRAN", - "Hash": "c8fbdbd9fcac223d6c6fe8e406f368e1" + "Hash": "c8fbdbd9fcac223d6c6fe8e406f368e1", + "Requirements": [ + "dplyr", + "purrr", + "rlang", + "tibble" + ] }, "webshot": { "Package": "webshot", "Version": "0.5.2", "Source": "Repository", "Repository": "CRAN", - "Hash": "e99d80ad34457a4853674e89d5e806de" + "Hash": "e99d80ad34457a4853674e89d5e806de", + "Requirements": [] }, "withr": { "Package": "withr", - "Version": "2.4.2", + "Version": "2.5.0", "Source": "Repository", "Repository": "CRAN", - "Hash": "ad03909b44677f930fa156d47d7a3aeb" + "Hash": "c0e49a9760983e81e55cdd9be92e7182", + "Requirements": [] }, "xfun": { "Package": "xfun", "Version": "0.30", "Source": "Repository", "Repository": "CRAN", - "Hash": "e83f48136b041845e50a6658feffb197" + "Hash": "e83f48136b041845e50a6658feffb197", + "Requirements": [] } } } diff --git a/srv_ui_elements/filter_UI.R b/srv_ui_elements/filter_UI.R index ce1b153..84d44cf 100644 --- a/srv_ui_elements/filter_UI.R +++ b/srv_ui_elements/filter_UI.R @@ -178,7 +178,7 @@ list( # # filter warnings - output$warnings_filter <- renderUI({ + output$warnings_filter_UI <- renderUI({ HTML(lapply(revals$warningmessage_filter, function(el){paste0("

")}) %>% paste(collapse = "")) }) diff --git a/srv_ui_elements/groups_UI.R b/srv_ui_elements/groups_UI.R index b62ba8b..f1df918 100644 --- a/srv_ui_elements/groups_UI.R +++ b/srv_ui_elements/groups_UI.R @@ -1,6 +1,6 @@ list( # sample names selector based on the sample names of revals$uploaded_data - output$group_samples <- renderUI({ + output$group_samples_UI <- renderUI({ validate(need(sample_names(), message = "Upload data before defining groups")) req(!is.null(input$group_regex)) # filter sample names diff --git a/srv_ui_elements/upload_UI_mainpanel.R b/srv_ui_elements/upload_UI_mainpanel.R index 75abba1..4c01729 100644 --- a/srv_ui_elements/upload_UI_mainpanel.R +++ b/srv_ui_elements/upload_UI_mainpanel.R @@ -42,7 +42,7 @@ list( }), # End edata_text # display list of warnings pasted on separate lines - output$warnings_upload <- renderUI({ + output$warnings_upload_UI <- renderUI({ HTML(lapply(revals$warningmessage_upload, function(el){ paste0("

") }) %>% diff --git a/srv_ui_elements/visualize_UI_misc.R b/srv_ui_elements/visualize_UI_misc.R index d04eedc..e0a5e85 100644 --- a/srv_ui_elements/visualize_UI_misc.R +++ b/srv_ui_elements/visualize_UI_misc.R @@ -1,6 +1,6 @@ list( # warning messages for viztab - output$warnings_visualize <- renderUI({ + output$warnings_visualize_UI <- renderUI({ HTML(paste(revals$warningmessage_visualize, collapse = "")) }), diff --git a/tab_factories/upload_tab.R b/tab_factories/upload_tab.R index cc5a783..0402733 100644 --- a/tab_factories/upload_tab.R +++ b/tab_factories/upload_tab.R @@ -143,7 +143,7 @@ upload_tab <- function(from_corems = FALSE) { column(width = 8, # warnings panel - div(id = "warnings_upload", style = "overflow-y:auto;max-height:250px", uiOutput("warnings_upload")), + div(id = "warnings_upload", style = "overflow-y:auto;max-height:250px", uiOutput("warnings_upload_UI")), tags$hr(), diff --git a/tests/testthat.R b/tests/testthat.R new file mode 100644 index 0000000..940e79c --- /dev/null +++ b/tests/testthat.R @@ -0,0 +1 @@ +shinytest2::test_app() \ No newline at end of file diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R new file mode 100644 index 0000000..155132d --- /dev/null +++ b/tests/testthat/setup.R @@ -0,0 +1,6 @@ +for (fname in Sys.glob("tab_navigation/*")) { + source(fname) +} + +# Load application support files into testing environment +shinytest2::load_app_env() diff --git a/tests/testthat/tab_navigation/basic_groups.R b/tests/testthat/tab_navigation/basic_groups.R new file mode 100644 index 0000000..839ab59 --- /dev/null +++ b/tests/testthat/tab_navigation/basic_groups.R @@ -0,0 +1,31 @@ +#'Create two groups using the example data. +#' +groups_basic <- function(app) { + app$click("goto_groups") + app$set_inputs(group_name = "G1") + app$set_inputs(group_samples = "EM0011_sample") + app$set_inputs(group_samples = c("EM0011_sample", "EM0013_sample")) + app$set_inputs(group_samples = c("EM0011_sample", "EM0013_sample", "EM0015_sample")) + app$set_inputs(group_samples = c("EM0011_sample", "EM0013_sample", "EM0015_sample", + "EM0017_sample")) + app$set_inputs(group_samples = c("EM0011_sample", "EM0013_sample", "EM0015_sample", + "EM0017_sample", "EM0019_sample")) + app$set_inputs(group_samples = c("EM0011_sample", "EM0013_sample", "EM0015_sample", + "EM0017_sample", "EM0019_sample", "EM0061_sample")) + app$set_inputs(group_samples = c("EM0011_sample", "EM0013_sample", "EM0015_sample", + "EM0017_sample", "EM0019_sample", "EM0061_sample", "EM0063_sample")) + app$set_inputs(group_samples = c("EM0011_sample", "EM0013_sample", "EM0015_sample", + "EM0017_sample", "EM0019_sample", "EM0061_sample", "EM0063_sample", "EM0065_sample")) + app$set_inputs(group_samples = c("EM0011_sample", "EM0013_sample", "EM0015_sample", + "EM0017_sample", "EM0019_sample", "EM0061_sample", "EM0063_sample", "EM0065_sample", + "EM0067_sample")) + app$set_inputs(group_samples = c("EM0011_sample", "EM0013_sample", "EM0015_sample", + "EM0017_sample", "EM0019_sample", "EM0061_sample", "EM0063_sample", "EM0065_sample", + "EM0067_sample", "EM0069_sample")) + app$click("add_group") + + app$set_inputs(group_name = "G2") + app$click("add_group") + + return(app) +} \ No newline at end of file diff --git a/tests/testthat/tab_navigation/upload_example.R b/tests/testthat/tab_navigation/upload_example.R new file mode 100644 index 0000000..e96e035 --- /dev/null +++ b/tests/testthat/tab_navigation/upload_example.R @@ -0,0 +1,32 @@ +#'Upload example data and fill in basic information on the uplaods tab +#' +upload_basic <- function(app) { + app$set_inputs(filter_sidebar = c("samplefilt_collapse", "massfilt_collapse", "molfilt_collapse", + "formfilt_collapse")) + app$set_inputs(select = character(0)) + app$set_inputs(isotope_yn = character(0)) + app$set_inputs(data_scale = "abundance") + app$set_inputs(upload_collapse = "file_upload") + app$set_inputs(file_edata = character(0)) + app$set_inputs(file_emeta = character(0)) + app$click("element_dropdown") + app$click("upload_click") + app$set_inputs(top_page = "data_requirements") + app$set_inputs(top_page = "Resources/Contact") + app$set_inputs(top_page = "Upload") + app$upload_file(file_edata = "../../Data/example12T_edata.csv") + app$wait_for_value(input="edata_id_col") + app$set_inputs(edata_id_col = "Mass") + app$upload_file(file_emeta = "../../Data/example12T_emeta.csv") + app$set_inputs(upload_collapse = "column_info") + app$set_inputs(select = "2") + app$click("element_dropdown") + app$set_inputs(element_dropdown_state = TRUE) + app$set_inputs(element_dropdown_state = FALSE) + app$set_inputs(isotope_yn = "2") + app$click("upload_click") + + btn_value = app$wait_for_value(input = "upload_dismiss") + + return(app) +} \ No newline at end of file diff --git a/tests/testthat/test_full_pipeline_basic.R b/tests/testthat/test_full_pipeline_basic.R new file mode 100644 index 0000000..accf63f --- /dev/null +++ b/tests/testthat/test_full_pipeline_basic.R @@ -0,0 +1,160 @@ +library(shinytest2) + +context("basic-workflow") + +test_that("Basic Tests", { + app <- AppDriver$new(name = "FREDA", height = 1199, width = 1299) + app <- upload_basic(app) + + ### Make Groups + message("Testing groups tab...") + + app <- groups_basic(app) + + groups_list <- app$get_value(export = "groups_list") + expect_setequal(groups_list$G1, c("EM0011_sample", "EM0013_sample", "EM0015_sample", + "EM0017_sample", "EM0019_sample", "EM0061_sample", + "EM0063_sample", "EM0065_sample", "EM0067_sample", + "EM0069_sample")) + expect_setequal(groups_list$G2, c("EW0111_sample", "EW0113_sample", "EW0115_sample", + "EW0117_sample", "EW0119_sample", "EW0161_sample", + "EW0163_sample", "EW0165_sample", "EW0167_sample", + "EW0169_sample")) + + message("Testing preprocess tab...") + ### Preprocessing + app$click("goto_preprocess_main") + app$wait_for_value(input = "tests") + app$set_inputs(tests = c("calc_element_ratios", "calc_kendrick", "calc_nosc")) + app$set_inputs(tests = c("calc_element_ratios", "calc_kendrick", "calc_nosc", "calc_gibbs")) + app$set_inputs(tests = c("calc_element_ratios", "calc_kendrick", "calc_nosc", "calc_gibbs", + "calc_aroma")) + app$set_inputs(tests = c("calc_element_ratios", "calc_kendrick", "calc_nosc", "calc_gibbs", + "calc_aroma", "calc_dbe")) + app$set_inputs(tests = c("calc_element_ratios", "calc_kendrick", "calc_nosc", "calc_gibbs", + "calc_aroma", "calc_dbe", "assign_elemental_composition")) + app$set_inputs(tests = c("calc_element_ratios", "calc_kendrick", "calc_nosc", "calc_gibbs", + "calc_aroma", "calc_dbe", "assign_elemental_composition", "assign_class;bs1")) + app$set_inputs(tests = c("calc_element_ratios", "calc_kendrick", "calc_nosc", "calc_gibbs", + "calc_aroma", "calc_dbe", "assign_elemental_composition", "assign_class;bs1", + "assign_class;bs2")) + app$set_inputs(tests = c("calc_element_ratios", "calc_kendrick", "calc_nosc", "calc_gibbs", + "calc_aroma", "calc_dbe", "assign_elemental_composition", "assign_class;bs1", + "assign_class;bs2", "assign_class;bs3")) + + app$wait_for_value(input = "base_unit") + app$set_inputs(base_unit = c("CH2", "CO2")) + app$set_inputs(base_unit = c("CH2", "CO2", "H2")) + app$set_inputs(base_unit = c("CH2", "CO2", "H2", "H2O")) + app$set_inputs(base_unit = c("CH2", "CO2", "H2", "H2O", "CHO")) + app$click("preprocess_click") + + app$wait_for_value(input = "preprocess_dismiss") + app$click("preprocess_dismiss") + + app$wait_for_value(output = "preprocess_hist") + + uploaded_data <- app$get_value(export = "uploaded_data_processed") + expected_cols = c('Mass', 'C', 'H', 'O', 'N', 'C13', 'S', 'P', 'Error', + 'NeutralMass', 'MolForm', 'OtoC_ratio', 'HtoC_ratio', + 'NtoC_ratio', 'PtoC_ratio', 'NtoP_ratio', 'kmass.CH2', + 'kdefect.CH2', 'NOSC', 'GFE', 'AI', 'AI_Mod', 'DBE_1', + 'DBE_O', 'DBE_AI', 'ElComposition', 'bs1_class', + 'bs2_class', 'bs3_class', 'kmass.CO2', 'kmass.H2', + 'kmass.H2O', 'kmass.CHO', 'kdefect.CO2', 'kdefect.H2', + 'kdefect.H2O', 'kdefect.CHO') + + expect_setequal(colnames(uploaded_data$e_meta), expected_cols) + + numtable <- app$get_value(export = "numeric_table") + cattable <- app$get_value(export = "categorical_table") + + expect_equal(digest::digest(numtable), "1d8dc9e8fe23e5579e8fda9d60fb92ca") + expect_equal(digest::digest(cattable), "78029809dbe2eeafe986ec9d68efb3f5") + + message("Testing filter tab...") + ### Filter + app$set_inputs(top_page = "Filter") + app$set_inputs(samplefilter = TRUE) + app$set_inputs(max_mass = 870) + app$set_inputs(massfilter = TRUE) + app$set_inputs(molfilter = TRUE) + app$set_inputs(formfilter = TRUE) + app$set_inputs(filter_sidebar = c("samplefilt_collapse", "massfilt_collapse", "molfilt_collapse", + "formfilt_collapse", "customfilt_collapse")) + app$set_inputs(customfilterz = TRUE) + app$set_inputs(filter_sidebar = c("samplefilt_collapse", "massfilt_collapse", "molfilt_collapse", + "formfilt_collapse", "customfilt_collapse", "customfilt_collapse")) + app$set_inputs(custom1 = "C") + + app$click("filter_click") + + btn_value = app$wait_for_value(input = "filter_dismiss") + app$click("filter_dismiss") + + message("Testing visualization tab...") + ### Visualization + app$click("goto_viz") + app$set_inputs(choose_single = "1") + app$click("plot_submit") + app$click("saveplot") + app$set_inputs(choose_single = "3") + app$set_inputs(summary_fxn = "uniqueness_gtest") + app$click("plot_submit") + app$click("saveplot") + app$set_inputs(summary_fxn = "uniqueness_nsamps") + app$click("plot_submit") + app$click("saveplot") + app$set_inputs(chooseplots = "Kendrick Plot") + app$set_inputs(choose_single = "2") + app$set_inputs(viz_sidebar = c("axlabs", "peakplots")) + app$set_inputs(whichSamples = "EM0011_sample") + app$set_inputs(whichSamples = c("EM0011_sample", "EM0013_sample")) + app$set_inputs(viz_sidebar = "axlabs") + app$set_inputs(whichSamples = c("EM0011_sample", "EM0013_sample", "EM0015_sample")) + app$set_inputs(whichSamples = c("EM0011_sample", "EM0013_sample", "EM0015_sample", + "EM0067_sample")) + app$set_inputs(whichSamples = c("EM0011_sample", "EM0013_sample", "EM0015_sample", + "EM0067_sample", "EW0111_sample")) + app$click("plot_submit") + app$click("saveplot") + app$set_inputs(choose_single = "1") + app$click("plot_submit") + app$set_inputs(top_page = "Glossary") + app$set_inputs(top_page = "Visualize") + app$set_inputs(chooseplots = "Kendrick Plot") + app$set_inputs(choose_single = "3") + app$set_inputs(summary_fxn = "uniqueness_gtest") + app$click("plot_submit") + app$click("saveplot") + app$set_inputs(chooseplots = "Custom Scatter Plot") + app$set_inputs(whichSamples = "EM0019_sample") + app$set_inputs(viz_sidebar = c("axlabs", "reactive_plot_opts")) + app$set_inputs(vk_colors = "uniqueness_gtest") + app$set_inputs(scatter_x = "N") + app$set_inputs(scatter_y = "HtoC_ratio") + app$click("plot_submit") + app$click("saveplot") + app$set_inputs(chooseplots = "PCOA Plot") + app$set_inputs(choose_dist = "gower") + app$set_inputs(title_input = "My") + app$set_inputs(title_input = "MyTitle") + app$click("plot_submit") + app$click("saveplot") + + ### Download + app$set_inputs(top_page = "Download") + app$set_inputs(download_selection = c("separate", "merged", "group_data")) + app$set_inputs(download_mappings = TRUE) + app$set_inputs(download_plot_table_rows_selected="1", allow_no_input_binding=TRUE) + app$click("mark_plot_download") + app$set_inputs(download_plot_table_rows_selected="3", allow_no_input_binding=TRUE) + app$click("mark_plot_download") + app$set_inputs(download_plot_table_rows_selected="4", allow_no_input_binding=TRUE) + app$click("mark_plot_download") + app$set_inputs(download_plot_table_rows_selected="5", allow_no_input_binding=TRUE) + app$click("mark_plot_download") + + app$set_inputs(download_img_width = 1500) + app$click("makezipfile") +}) diff --git a/tests/testthat/test_upload.R b/tests/testthat/test_upload.R new file mode 100644 index 0000000..99f52c8 --- /dev/null +++ b/tests/testthat/test_upload.R @@ -0,0 +1,35 @@ +library(shinytest2) + +test_that("testing the upload tab", { + context("basic-upload") + app <- AppDriver$new(name = "FREDA", height = 1199, width = 800) + app <- upload_basic(app) + + app$click("upload_dismiss") + + # Check that some inputs are corretly set + c_col <- app$get_value(input = "c_column") + n_col <- app$get_value(input = "n_column") + s_col <- app$get_value(input = "s_column") + h_col <- app$get_value(input = "h_column") + o_col <- app$get_value(input = "o_column") + p_col <- app$get_value(input = "p_column") + + expect_equal(c_col, "C") + expect_equal(n_col, "N") + expect_equal(s_col, "S") + expect_equal(h_col, "H") + expect_equal(o_col, "O") + expect_equal(p_col, "P") + + # Check data successfully stored and properties are correct. + uploaded_data <- app$get_value(export="uploaded_data") + expect_s3_class(uploaded_data, c("peakData", "ftmsData")) + print(all(dim(uploaded_data$e_data) == c(24442, 21))) + expect_true(all(dim(uploaded_data$e_data) == c(24442, 21))) + expect_true(all(dim(uploaded_data$f_data) == c(20, 2))) + expect_true(all(dim(uploaded_data$e_meta) == c(24442, 11))) + + mysumm <- summary(uploaded_data) + expect_equal(mysumm$Percent_Missing, 81.63714) +}) diff --git a/ui.R b/ui.R index 57c1112..67a9a09 100644 --- a/ui.R +++ b/ui.R @@ -32,7 +32,7 @@ ui <- tagList(useShinyjs(), navbarPage( tags$h4("Define a Group"), div(id = "js_group_name", textInput("group_name", "Name of this group:")), fluidRow( - column(6, uiOutput("group_samples")), + column(6, uiOutput("group_samples_UI")), column(6, textInput("group_regex", "Search sample names")) ), actionButton("add_group", "Add this group"), @@ -247,7 +247,7 @@ ui <- tagList(useShinyjs(), navbarPage( br(), br(), - div(id = "warnings_filter", style = "overflow-y:auto;max-height:150px", uiOutput("warnings_filter")) + div(id = "warnings_filter", style = "overflow-y:auto;max-height:150px", uiOutput("warnings_filter_UI")) ), # End sidebar panel on Filter tab @@ -280,7 +280,7 @@ ui <- tagList(useShinyjs(), navbarPage( fluidRow( # Sidebar Panel - div(id='viz_sidebar', column(4, + div(id='viz_sidebar_column', column(4, # Begin collapsible section bsCollapse(id='viz_sidebar', open = c('peakplots', 'axlabs'), multiple=TRUE, @@ -383,7 +383,7 @@ ui <- tagList(useShinyjs(), navbarPage( br(), br(), - div(id = "warnings_visualize", style = "overflow-y:auto;max-height:150px", uiOutput("warnings_visualize")) + div(id = "warnings_visualize", style = "overflow-y:auto;max-height:150px", uiOutput("warnings_visualize_UI")) ), )# End main panel on Visualize tab # )# end fluidrow @@ -415,7 +415,7 @@ ui <- tagList(useShinyjs(), navbarPage( tabPanel(div('Database Mapping', icon('th-list', lib = 'glyphicon')), value = 'Database Mapping', fluidRow(style = "display:flex;flex-direction:row;align-items:stretch", column(4, - bsCollapse(id='viz_sidebar', open = c('mappings'), multiple=TRUE, + bsCollapse(id='db_mapping_sidebar', open = c('mappings'), multiple=TRUE, bsCollapsePanel('Choose which mappings to calculate', value = 'mappings', radioGroupButtons('database_select', label = "Choose a Database", choices = c('Kegg', 'MetaCyc')), numericInput('max_records_database', 'Exclude formulae that map to more than this many records:', value = 5),