diff --git a/R/app_server.R b/R/app_server.R
index 5f036c454..1f4d1e0fc 100644
--- a/R/app_server.R
+++ b/R/app_server.R
@@ -32,12 +32,19 @@ app_server <- function(input, output, session) {
shinyjs::disable(selector = '.nav li a[data-value="Figures"]')
shinyjs::disable(selector = '.nav li a[data-value="Review"]')
+ # switch that indicates when a file is being loaded
+ tadat$load_progress_file = NA
+ tadat$save_progress_file = NA
+ job_id = paste0("ts", format(Sys.time(), "%y%m%d%H%M%S"))
+ tadat$default_outfile = paste0("tada_output_", job_id)
+ tadat$job_id = job_id
+
# switch to overview tab when tadat$new changes and provide user with window letting them know how many records were automatically flagged for removal upon upload
# move this to query_data?
shiny::observeEvent(tadat$new, {
shiny::showModal(shiny::modalDialog(
title = "Data Loaded",
- "Your data were successfully loaded into the app and are displayed on the Overview tab. See summary information about your dataset in the gray box at the bottom of the app."
+ "Your data were successfully loaded into the app and are displayed on the Overview tab. The following data wrangling steps were performed automatically when data was loaded: 1) created TADA versions of a subset of columns for editing (originals are retained), 2) removed exact duplicates, 3) handled/flagged special characters and text in result values and units, 4) identified detection limit data and copied limit value to result value if blank, 5) harmonized result and depth units to TADA defaults, and 6) replaced retired characteristic names with current names. See summary information about your dataset in the gray box at the bottom of the app."
))
shiny::updateTabsetPanel(session = session, inputId = "tabbar", selected = "Overview")
tadat$new <- NULL
@@ -50,6 +57,12 @@ app_server <- function(input, output, session) {
}
})
+ # Update the default switches if a progress file is uploaded.
+ shiny::observeEvent(tadat$selected_flags, {
+ switch_defaults = tadat$selected_flags
+ })
+
+
# this observes when the user switches tabs and adds the current tab they're on as a column to their dataset.
shiny::observe({
shiny::req(tadat$raw)
diff --git a/R/golem_utils_ui.R b/R/golem_utils_ui.R
index 0d11ee4e7..c91475023 100644
--- a/R/golem_utils_ui.R
+++ b/R/golem_utils_ui.R
@@ -230,6 +230,7 @@ with_red_star <- function(text) {
#' @examples
#' rep_br(5)
#' @importFrom shiny HTML
+
rep_br <- function(times = 1) {
HTML(rep("
", times = times))
}
diff --git a/R/mod_TADA_summary.R b/R/mod_TADA_summary.R
index 9d96d92df..d3578e3a9 100644
--- a/R/mod_TADA_summary.R
+++ b/R/mod_TADA_summary.R
@@ -38,9 +38,24 @@ mod_TADA_summary_ui <- function(id) {
)))),
shiny::fluidRow(column(6, shiny::uiOutput(ns(
"dwn_all"
- ))))
+ )))),
+ shiny::fluidRow(column(6, shiny::uiOutput(ns(
+ "dwn_ts"
+ ))))#,
+ # shiny::fluidRow(column(
+ # 6,
+ # shiny::fileInput(
+ # ns("up_ts"),
+ # "",
+ # multiple = TRUE,
+ # accept = ".Rdata",
+ # width = "100%"
+ # )
+ # ))
),
- shiny::fluidRow(column(2, shiny::actionButton(ns("disclaimer"), "DISCLAIMER"))),
+ shiny::fluidRow(column(
+ 2, shiny::actionButton(ns("disclaimer"), "DISCLAIMER")
+ )),
htmltools::br(),
htmltools::br()
),
@@ -60,20 +75,25 @@ mod_TADA_summary_server <- function(id, tadat) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
# reactive list to hold reactive objects specific to this module
- summary_things <- shiny::reactiveValues()
-
+ summary_things = shiny::reactiveValues()
+
+
# calculate the stats needed to fill the summary box
shiny::observe({
shiny::req(tadat$raw)
- summary_things$rem_rec <- length(tadat$raw$ResultIdentifier[tadat$raw$TADA.Remove ==
- TRUE])
- summary_things$clean_rec <- length(tadat$raw$ResultIdentifier[tadat$raw$TADA.Remove ==
- FALSE])
- clean_sites <- unique(tadat$raw$MonitoringLocationIdentifier[tadat$raw$TADA.Remove ==
- FALSE])
+ summary_things$rem_rec <-
+ length(tadat$raw$ResultIdentifier[tadat$raw$TADA.Remove ==
+ TRUE])
+ summary_things$clean_rec <-
+ length(tadat$raw$ResultIdentifier[tadat$raw$TADA.Remove ==
+ FALSE])
+ clean_sites <-
+ unique(tadat$raw$MonitoringLocationIdentifier[tadat$raw$TADA.Remove ==
+ FALSE])
summary_things$clean_site <- length(clean_sites)
- summary_things$rem_site <- length(unique(tadat$raw$MonitoringLocationIdentifier[!tadat$raw$MonitoringLocationIdentifier %in%
- clean_sites]))
+ summary_things$rem_site <-
+ length(unique(tadat$raw$MonitoringLocationIdentifier[!tadat$raw$MonitoringLocationIdentifier %in%
+ clean_sites]))
summary_things$removals <- sort_removals(tadat$removals)
})
summary_things$removals <- data.frame(matrix(
@@ -81,7 +101,7 @@ mod_TADA_summary_server <- function(id, tadat) {
nrow = 0,
dimnames = list(NULL, c("Reason", "Count"))
))
-
+
# output$removal_summary = DT::renderDataTable(
# summary_things$removals,
# escape = FALSE,
@@ -92,7 +112,7 @@ mod_TADA_summary_server <- function(id, tadat) {
# language = list(zeroRecords = "No records removed")
# )
# )
-
+
# summary text = total records
output$rec_tot <- shiny::renderText({
if (is.null(tadat$raw)) {
@@ -106,10 +126,8 @@ mod_TADA_summary_server <- function(id, tadat) {
if (is.null(tadat$raw)) {
"Total Results Flagged for Removal: 0"
} else {
- paste0(
- "Total Results Flagged for Removal: ",
- scales::comma(summary_things$rem_rec)
- )
+ paste0("Total Results Flagged for Removal: ",
+ scales::comma(summary_things$rem_rec))
}
})
# summary text = total records in clean
@@ -117,10 +135,8 @@ mod_TADA_summary_server <- function(id, tadat) {
if (is.null(tadat$raw)) {
"Total Results Retained: 0"
} else {
- paste0(
- "Total Results Retained: ",
- scales::comma(summary_things$clean_rec)
- )
+ paste0("Total Results Retained: ",
+ scales::comma(summary_things$clean_rec))
}
})
# summary text = total sites
@@ -138,10 +154,8 @@ mod_TADA_summary_server <- function(id, tadat) {
if (is.null(tadat$raw)) {
"Total Sites Flagged for Removal: 0"
} else {
- paste0(
- "Total Sites Flagged for Removal: ",
- scales::comma(summary_things$rem_site)
- )
+ paste0("Total Sites Flagged for Removal: ",
+ scales::comma(summary_things$rem_site))
}
})
# summary text = total sites in clean file
@@ -149,36 +163,64 @@ mod_TADA_summary_server <- function(id, tadat) {
if (is.null(tadat$raw)) {
"Total Sites Retained: 0"
} else {
- paste0(
- "Total Sites Retained: ",
- scales::comma(summary_things$clean_site)
- )
+ paste0("Total Sites Retained: ",
+ scales::comma(summary_things$clean_site))
}
})
-
- # download dataset button - only appears if there exists data in the app already
+
+ # download dataset button - only appears if there data exists in the app already
output$dwn_all <- shiny::renderUI({
shiny::req(tadat$raw)
shiny::downloadButton(ns("download_all"),
- "Download Working Dataset (.xlsx)",
- style = "color: #fff; background-color: #337ab7; border-color: #2e6da4"
- )
+ "Download Working Dataset (.zip)",
+ style = "color: #fff; background-color: #337ab7; border-color: #2e6da4")
})
-
+
output$download_all <- shiny::downloadHandler(
filename = function() {
- paste("TADAShiny_datadownload_", tadat$tab, ".xlsx", sep = "")
+ paste0(tadat$default_outfile, ".zip")
},
- content = function(file) {
- writexl::write_xlsx(TADA::TADA_OrderCols(tadat$raw), path = file)
- }
+ content = function(fname) {
+ fs <- c()
+ tmpdir <- tempdir()
+ setwd(tempdir())
+ datafile_name = paste0(tadat$default_outfile, ".xlsx")
+ progress_file_name = paste0(tadat$default_outfile, "_prog.RData")
+ desc <- writeNarrativeDataFrame(tadat)
+ dfs <-
+ list(Data = TADA::TADA_OrderCols(tadat$raw), Parameterization = desc)
+ writeFile(tadat, progress_file_name)
+ writexl::write_xlsx(dfs, path = datafile_name)
+ utils::zip(zipfile = fname,
+ files = c(datafile_name, progress_file_name))
+ },
+ contentType = "application/zip"
)
-
+
+ # # Download TADA progress file
+ # output$dwn_ts = shiny::renderUI({
+ # shiny::req(tadat$raw)
+ # shiny::downloadButton(ns("download_ts_file"),
+ # "Download Progress File (.Rdata)",
+ # style = "color: #fff; background-color: #337ab7; border-color: #2e6da4")
+ # })
+
+ # output$download_ts_file = shiny::downloadHandler(
+ # filename = function() {
+ # paste0(tadat$job_id, '.Rdata')
+ # },
+ # content = function(file) {
+ # writeFile(tadat, file)
+ # }
+ # )
+
shiny::observeEvent(input$disclaimer, {
- shiny::showModal(shiny::modalDialog(
- title = "Disclaimer",
- "This United States Environmental Protection Agency (EPA) GitHub project code is provided on an 'as is' basis and the user assumes responsibility for its use. EPA has relinquished control of the information and no longer has responsibility to protect the integrity, confidentiality, or availability of the information. Any reference to specific commercial products, processes, or services by service mark, trademark, manufacturer, or otherwise, does not constitute or imply their endorsement, recommendation or favoring by EPA. The EPA seal and logo shall not be used in any manner to imply endorsement of any commercial product or activity by EPA or the United States Government."
- ))
+ shiny::showModal(
+ shiny::modalDialog(
+ title = "Disclaimer",
+ "This United States Environmental Protection Agency (EPA) GitHub project code is provided on an 'as is' basis and the user assumes responsibility for its use. EPA has relinquished control of the information and no longer has responsibility to protect the integrity, confidentiality, or availability of the information. Any reference to specific commercial products, processes, or services by service mark, trademark, manufacturer, or otherwise, does not constitute or imply their endorsement, recommendation or favoring by EPA. The EPA seal and logo shall not be used in any manner to imply endorsement of any commercial product or activity by EPA or the United States Government."
+ )
+ )
})
})
}
@@ -194,23 +236,26 @@ sort_removals <- function(removal_table) {
))
colnames(results) <- prefixes
results[is.na(results)] <- FALSE
-
+
for (prefix in prefixes) {
active_cols <- fields[dplyr::starts_with(prefix, vars = fields)]
if (length(active_cols) > 0) {
- results[prefix] <- apply(dplyr::select(removal_table, active_cols), 1, any)
+ results[prefix] <-
+ apply(dplyr::select(removal_table, active_cols), 1, any)
}
}
totals <- rowSums(results)
results["Flag only"] <- ((totals == 1) & results$Flag)
results["Flag and Filter"] <- (results$Flag & results$Filter)
results["Filter only"] <- ((totals == 1) & results$Filter)
- results <- dplyr::select(results, -intersect(prefixes, colnames(results)))
+ results <-
+ dplyr::select(results,-intersect(prefixes, colnames(results)))
results$Many <- rowSums(results) > 2
results$Retained <- !apply(results, 1, any)
counts <- colSums(results)
- counts <- data.frame(Reason = names(counts), Count = as.vector(counts))
- counts <- counts[(counts$Count > 0), ]
+ counts <-
+ data.frame(Reason = names(counts), Count = as.vector(counts))
+ counts <- counts[(counts$Count > 0),]
return(counts)
}
}
diff --git a/R/mod_censored_data.R b/R/mod_censored_data.R
index f712c2c57..805105fff 100644
--- a/R/mod_censored_data.R
+++ b/R/mod_censored_data.R
@@ -7,36 +7,85 @@
#' @noRd
#'
#' @importFrom shiny NS tagList
+
+nd_method_options <-
+ c("Multiply detection limit by x",
+ "Random number between 0 and detection limit",
+ "No change")
+od_method_options <- c("Multiply detection limit by x", "No change")
+
mod_censored_data_ui <- function(id) {
ns <- NS(id)
tagList(
shiny::fluidRow(htmltools::h3("Censored Data Categories")),
- shiny::fluidRow("TADAdataRetrieval assigns each result in your dataset to non-detect, over-detect, other, or uncensored. The pie chart below displays the relative proportions of results in each category. Please note that detection limit data with conflicts or data quality issues are not displayed in this pie chart or handled in the methods below."),
+ shiny::fluidRow(
+ "TADAdataRetrieval assigns each result in your dataset to non-detect, over-detect, other, or uncensored. The pie chart below displays the relative proportions of results in each category. Please note that detection limit data with conflicts or data quality issues are not displayed in this pie chart or handled in the methods below."
+ ),
htmltools::br(),
- shiny::fluidRow(column(12, shiny::plotOutput(ns("id_censplot")))),
+ shiny::fluidRow(column(12, shiny::plotOutput(
+ ns("id_censplot")
+ ))),
htmltools::br(),
- shiny::fluidRow(htmltools::h3("Handle Censored Data Using Simple Methods")),
- shiny::fluidRow("Use the drop down menus below to pick a simple method for handling non-detects and over-detects in the dataset. When you press 'Apply Methods to Dataset', a table will appear below with the first 10 detection limit results, showing their initial values and estimated values."),
+ shiny::fluidRow(htmltools::h3(
+ "Handle Censored Data Using Simple Methods"
+ )),
+ shiny::fluidRow(
+ "Use the drop down menus below to pick a simple method for handling non-detects and over-detects in the dataset. When you press 'Apply Methods to Dataset', a table will appear below with the first 10 detection limit results, showing their initial values and estimated values."
+ ),
htmltools::br(),
shiny::fluidRow(
- column(3, shiny::selectInput(ns("nd_method"), "Non-Detect Handling Method", choices = c("Multiply detection limit by x", "Random number between 0 and detection limit", "No change"), multiple = FALSE)),
+ column(
+ 3,
+ shiny::selectizeInput(
+ ns("nd_method"),
+ "Non-Detect Handling Method",
+ choices = nd_method_options,
+ selected = nd_method_options[1],
+ multiple = TRUE,
+ options = list(maxItems = 1)
+ )
+ ),
column(3, shiny::uiOutput(ns("nd_mult"))),
- column(3, shiny::selectInput(ns("od_method"), "Over-Detect Handling Method", choices = c("Multiply detection limit by x", "No change"), selected = "No change", multiple = FALSE)),
+ column(
+ 3,
+ shiny::selectizeInput(
+ ns("od_method"),
+ "Over-Detect Handling Method",
+ choices = od_method_options,
+ selected = od_method_options[2],
+ multiple = TRUE,
+ options = list(maxItems = 1)
+ )
+ ),
column(3, shiny::uiOutput(ns("od_mult")))
),
shiny::fluidRow(
- column(3, shiny::actionButton(ns("apply_methods"), "Apply Methods to Dataset", style = "color: #fff; background-color: #337ab7; border-color: #2e6da4")),
+ column(
+ 3,
+ shiny::actionButton(ns("apply_methods"), "Apply Methods to Dataset", style = "color: #fff; background-color: #337ab7; border-color: #2e6da4")
+ ),
column(3, shiny::uiOutput(ns("undo_methods")))
),
htmltools::br(),
- shiny::fluidRow(column(12, DT::DTOutput(ns("see_det")))),
+ shiny::fluidRow(column(12, DT::DTOutput(ns(
+ "see_det"
+ )))),
htmltools::br(),
- shiny::fluidRow(htmltools::h3("Consider More Complex Censored Data Handling Methods")),
- shiny::fluidRow("Use the picker list below to select grouping columns to create summary table. The summary table shows the number of non- and over-detects in each group, the total number of results in each group, the number of detection limit types (censoring levels) and the percentage of the dataset that is censored. These numbers are then used to suggest a potential statistical censored data method to use. Currently, the user must perform more complex analyses outside of TADAShiny."),
+ shiny::fluidRow(
+ htmltools::h3("Consider More Complex Censored Data Handling Methods")
+ ),
+ shiny::fluidRow(
+ "Use the picker list below to select grouping columns to create summary table. The summary table shows the number of non- and over-detects in each group, the total number of results in each group, the number of detection limit types (censoring levels) and the percentage of the dataset that is censored. These numbers are then used to suggest a potential statistical censored data method to use. Currently, the user must perform more complex analyses outside of TADAShiny."
+ ),
htmltools::br(),
shiny::fluidRow(shiny::wellPanel(
- shiny::fluidRow(column(12, shiny::uiOutput(ns("cens_groups")))),
- shiny::fluidRow(column(12, shiny::actionButton(ns("cens_sumbutton"), "ID and Summarize Censored Data", style = "color: #fff; background-color: #337ab7; border-color: #2e6da4")))
+ shiny::fluidRow(column(12, shiny::uiOutput(
+ ns("cens_groups")
+ ))),
+ shiny::fluidRow(column(
+ 12,
+ shiny::actionButton(ns("cens_sumbutton"), "ID and Summarize Censored Data", style = "color: #fff; background-color: #337ab7; border-color: #2e6da4")
+ ))
)),
shiny::fluidRow(DT::DTOutput(ns("cens_sumtable")), width = 600)
)
@@ -48,10 +97,12 @@ mod_censored_data_ui <- function(id) {
mod_censored_data_server <- function(id, tadat) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
-
+
+ # initialize dropdown values
+
# reactive values specific to this module
censdat <- shiny::reactiveValues()
-
+
# update dataset when on censored data page
shiny::observeEvent(tadat$tab, {
shiny::req(tadat$raw)
@@ -64,42 +115,107 @@ mod_censored_data_server <- function(id, tadat) {
# paste0(length(dat$ResultIdentifier[dat$TADA.Remove==TRUE])," results were flagged for removal because they have conflicting, ambiguous and/or unfamiliar detection limits and conditions. These will show up in the pie chart, but only 'Non-Detect', 'Over-Detect', and 'Uncensored' results will be used in the sections below. You may download your dataset for review at any time using the 'Download Working Dataset' button at the bottom of the page.")
# ))
# }
- censdat$dat <- subset(tadat$raw, tadat$raw$TADA.Remove == FALSE) # however, this reactive object has all of the data that were not previously removed and do not have ambiguous detection limit data. This is the "clean" dataset
+ censdat$dat <-
+ subset(tadat$raw, tadat$raw$TADA.Remove == FALSE) # however, this reactive object has all of the data that were not previously removed and do not have ambiguous detection limit data. This is the "clean" dataset
}
})
-
+
# pie chart showing breakdown of censored/uncensored data passed through idCensoredData function
output$id_censplot <- shiny::renderPlot({
shiny::req(censdat$dat)
piedat <- censdat$dat %>%
dplyr::group_by(TADA.CensoredData.Flag) %>%
dplyr::summarise(num = length(ResultIdentifier))
- piedat$Label <- paste0(piedat$TADA.CensoredData.Flag, " - ", scales::comma(piedat$num), " results")
+ piedat$Label <-
+ paste0(piedat$TADA.CensoredData.Flag,
+ " - ",
+ scales::comma(piedat$num),
+ " results")
# Basic piechart
ggplot2::ggplot(piedat, ggplot2::aes(x = "", y = num, fill = Label)) +
- ggplot2::geom_bar(stat = "identity", width = 1, color = "white") +
+ ggplot2::geom_bar(stat = "identity",
+ width = 1,
+ color = "white") +
ggplot2::labs(title = "Number of Results per Censored Data Category") +
ggplot2::coord_polar("y", start = 0) +
ggplot2::scale_fill_brewer(palette = "Dark2") +
ggplot2::theme_void() + # remove background, grid, numeric labels
- ggplot2::theme(plot.title = ggplot2::element_text(face = "bold", size = 18), legend.title = ggplot2::element_text(size = 16), legend.text = ggplot2::element_text(size = 14)) #+
+ ggplot2::theme(
+ plot.title = ggplot2::element_text(face = "bold", size = 18),
+ legend.title = ggplot2::element_text(size = 16),
+ legend.text = ggplot2::element_text(size = 14)
+ ) #+
# ggplot2::geom_text(ggplot2::aes(label = scales::comma(num)), color = "white", size=6,position = ggplot2::position_stack(vjust = 0.5))
})
-
+
+
# this adds the multiplier numeric input next to the method selection if the nd method selected is to mult det limit by x
+
output$nd_mult <- shiny::renderUI({
- if (input$nd_method == "Multiply detection limit by x") {
- shiny::numericInput(ns("nd_mult"), "Multiplier (x)", value = 0.5, min = 0)
+ init_val = tadat$nd_mult
+ if (is.null(init_val)){
+ init_val = 0.5
+ }
+ if (input$nd_method == nd_method_options[1]) {
+ shiny::numericInput(ns("nd_mult"),
+ "Multiplier (x)",
+ value = init_val,
+ min = 0)
}
})
-
+
# this adds the multiplier numeric input next to the method selection if the od method selected is to mult det limit by x
output$od_mult <- shiny::renderUI({
- if (input$od_method == "Multiply detection limit by x") {
- shiny::numericInput(ns("od_mult"), "Multiplier (x)", value = 1, min = 0)
+ init_val = tadat$od_mult
+ if (is.null(init_val)){
+ init_val = 0.5
+ }
+ if (input$od_method == od_method_options[1]) {
+ shiny::numericInput(ns("od_mult"),
+ "Multiplier (x)",
+ value = init_val,
+ min = 0)
}
})
-
+
+
+ # initialize global variables for saving/loading
+
+ tadat$censor_applied = FALSE
+
+ shiny::observeEvent(tadat$load_progress_file, {
+ if (!is.na(tadat$load_progress_file)) {
+ shiny::updateSelectizeInput(session,
+ "nd_method",
+ choices = nd_method_options,
+ selected = tadat$nd_method)
+ shiny::updateSelectizeInput(session,
+ "od_method",
+ choices = od_method_options,
+ selected = tadat$od_method)
+ shiny::updateNumericInput(session, "nd_mult", value = tadat$nd_mult)
+ shiny::updateNumericInput(session, "od_mult", value = tadat$od_mult)
+ }
+ })
+
+ # Make this part more concise?
+ shiny::observeEvent(input$nd_method, {
+ tadat$nd_method = input$nd_method
+ })
+
+ shiny::observeEvent(input$nd_mult, {
+ tadat$nd_mult = input$nd_mult
+ })
+
+ shiny::observeEvent(input$od_method, {
+ tadat$od_method = input$od_method
+ })
+
+ shiny::observeEvent(input$od_mult, {
+ tadat$od_mult = input$od_mult
+ })
+
+
# Button to apply the simple methods to the nd and od results in the dataset.
shiny::observeEvent(input$apply_methods, {
shinybusy::show_modal_spinner(
@@ -108,10 +224,17 @@ mod_censored_data_server <- function(id, tadat) {
text = "Applying selected methods...",
session = shiny::getDefaultReactiveDomain()
)
- removed <- subset(tadat$raw, tadat$raw$TADA.Remove == TRUE) # first, remove results we dont want to handle at all
- good <- subset(tadat$raw, tadat$raw$TADA.Remove == FALSE) # keep the "goods" that will be run through the simpleCensoredMethods function
- trans <- data.frame(input = c("Multiply detection limit by x", "Random number between 0 and detection limit", "No change"), actual = c("multiplier", "randombelowlimit", "as-is"))
- if (is.null(input$nd_mult)) { # these if's get the reactive inputs into a format that the TADA function will understand
+ removed <-
+ subset(tadat$raw, tadat$raw$TADA.Remove == TRUE) # first, remove results we dont want to handle at all
+ good <-
+ subset(tadat$raw, tadat$raw$TADA.Remove == FALSE) # keep the "goods" that will be run through the simpleCensoredMethods function
+ trans <-
+ data.frame(
+ input = nd_method_options,
+ actual = c("multiplier", "randombelowlimit", "as-is")
+ )
+ if (is.null(input$nd_mult)) {
+ # these if's get the reactive inputs into a format that the TADA function will understand
nd_multiplier <- "null"
} else {
nd_multiplier <- input$nd_mult
@@ -121,41 +244,81 @@ mod_censored_data_server <- function(id, tadat) {
} else {
od_multiplier <- input$od_mult
}
- good <- TADA::TADA_SimpleCensoredMethods(good, nd_method = trans$actual[trans$input == input$nd_method], nd_multiplier = nd_multiplier, od_method = trans$actual[trans$input == input$od_method], od_multiplier = od_multiplier)
- tadat$raw <- plyr::rbind.fill(removed, good) # stitch good and removed datasets back together in tadat$raw
+ good <-
+ TADA::TADA_SimpleCensoredMethods(
+ good,
+ nd_method = trans$actual[trans$input == input$nd_method],
+ nd_multiplier = nd_multiplier,
+ od_method = trans$actual[trans$input == input$od_method],
+ od_multiplier = od_multiplier
+ )
+ tadat$raw <-
+ plyr::rbind.fill(removed, good) # stitch good and removed datasets back together in tadat$raw
tadat$raw <- TADA::TADA_OrderCols(tadat$raw)
-
+
# create dataset displayed in table below
- dat <- subset(good, good$TADA.CensoredData.Flag %in% c("Non-Detect", "Over-Detect"))
- dat <- dat[, c("ResultIdentifier", "TADA.CharacteristicName", "TADA.DetectionQuantitationLimitMeasure.MeasureValue", "DetectionQuantitationLimitMeasure.MeasureUnitCode", "TADA.ResultMeasureValue", "TADA.ResultMeasure.MeasureUnitCode")]
- dat <- dat %>% dplyr::rename("Estimated Detection Limit Value" = TADA.ResultMeasureValue, "Original Detection Limit Value" = TADA.DetectionQuantitationLimitMeasure.MeasureValue, "Original Unit" = DetectionQuantitationLimitMeasure.MeasureUnitCode, "Estimated Unit" = TADA.ResultMeasure.MeasureUnitCode)
- censdat$exdat <- dat[1:10, ] # just show the first 10 records so user can see what happened to data
+ dat <-
+ subset(good,
+ good$TADA.CensoredData.Flag %in% c("Non-Detect", "Over-Detect"))
+ dat <-
+ dat[, c(
+ "ResultIdentifier",
+ "TADA.CharacteristicName",
+ "TADA.DetectionQuantitationLimitMeasure.MeasureValue",
+ "DetectionQuantitationLimitMeasure.MeasureUnitCode",
+ "TADA.ResultMeasureValue",
+ "TADA.ResultMeasure.MeasureUnitCode"
+ )]
+ dat <-
+ dat %>% dplyr::rename(
+ "Estimated Detection Limit Value" = TADA.ResultMeasureValue,
+ "Original Detection Limit Value" = TADA.DetectionQuantitationLimitMeasure.MeasureValue,
+ "Original Unit" = DetectionQuantitationLimitMeasure.MeasureUnitCode,
+ "Estimated Unit" = TADA.ResultMeasure.MeasureUnitCode
+ )
+ censdat$exdat <-
+ dat[1:10, ] # just show the first 10 records so user can see what happened to data
shinybusy::remove_modal_spinner(session = shiny::getDefaultReactiveDomain())
+ tadat$censor_applied = TRUE
})
-
+
# this button appears after someone has applied the OD/ND methods, in case they want to undo and try another method instead
output$undo_methods <- shiny::renderUI({
shiny::req(censdat$exdat)
shiny::actionButton(ns("undo_methods"), "Undo Method Application", style = "color: #fff; background-color: #337ab7; border-color: #2e6da4")
})
-
+
# executes the undo if undo methods button is pressed.
shiny::observeEvent(input$undo_methods, {
censdat$exdat <- NULL # reset exdat
- tadat$raw$TADA.ResultMeasureValue <- ifelse(tadat$raw$TADA.ResultMeasureValueDataTypes.Flag == "Result Value/Unit Estimated from Detection Limit", tadat$raw$TADA.DetectionQuantitationLimitMeasure.MeasureValue, tadat$raw$TADA.ResultMeasureValue) # reset to detection quantitation limit value
- tadat$raw$TADA.ResultMeasureValueDataTypes.Flag[tadat$raw$TADA.ResultMeasureValueDataTypes.Flag == "Result Value/Unit Estimated from Detection Limit"] <- "Result Value/Unit Copied from Detection Limit" # reset data types flag to what it was before simpleCensoredMethods function run
+ tadat$raw$TADA.ResultMeasureValue <-
+ ifelse(
+ tadat$raw$TADA.ResultMeasureValueDataTypes.Flag == "Result Value/Unit Estimated from Detection Limit",
+ tadat$raw$TADA.DetectionQuantitationLimitMeasure.MeasureValue,
+ tadat$raw$TADA.ResultMeasureValue
+ ) # reset to detection quantitation limit value
+ tadat$raw$TADA.ResultMeasureValueDataTypes.Flag[tadat$raw$TADA.ResultMeasureValueDataTypes.Flag == "Result Value/Unit Estimated from Detection Limit"] <-
+ "Result Value/Unit Copied from Detection Limit" # reset data types flag to what it was before simpleCensoredMethods function run
tadat$raw <- tadat$raw %>% dplyr::select(-TADA.CensoredMethod)
+ tadat$censor_applied = FALSE
})
-
+
# creates a nice table showing an example of how censored data were changed.
output$see_det <- DT::renderDT({
shiny::req(censdat$exdat)
- DT::datatable(censdat$exdat[1:10, ],
- options = list(dom = "t", scrollX = TRUE, pageLength = 10, searching = FALSE),
- selection = "none", rownames = FALSE
+ DT::datatable(
+ censdat$exdat[1:10, ],
+ options = list(
+ dom = "t",
+ scrollX = TRUE,
+ pageLength = 10,
+ searching = FALSE
+ ),
+ selection = "none",
+ rownames = FALSE
)
})
-
+
# from the clean dataset, get all of the column names someone might want to group by when summarizing their data for use in more advanced censored data methods.
output$cens_groups <- shiny::renderUI({
shiny::req(censdat$dat)
@@ -168,34 +331,62 @@ mod_censored_data_server <- function(id, tadat) {
"TADA.DetectionQuantitationLimitMeasure.MeasureValue",
"DetectionQuantitationLimitMeasure.MeasureValue"
)] # remove the columns that are generally unique to each result from consideration. Why would someone want to group by result value or identifier? Then every summary would be unique to one value...not a "summary"
- tcols <- ccols[grepl("TADA.", ccols)] # put all of the TADA columns at the top of the selection drop down
- ucols <- ccols[!grepl("TADA.", ccols)] # then have the WQP columns
- ccols <- c(tcols, ucols) # string them back together in one vector used in the selection widget below
- shiny::selectizeInput(ns("cens_groups"),
+ tcols <-
+ ccols[grepl("TADA.", ccols)] # put all of the TADA columns at the top of the selection drop down
+ ucols <-
+ ccols[!grepl("TADA.", ccols)] # then have the WQP columns
+ ccols <-
+ c(tcols, ucols) # string them back together in one vector used in the selection widget below
+ shiny::selectizeInput(
+ ns("cens_groups"),
label = "Select Grouping Columns for Summarization",
- choices = ccols, selected = c("TADA.ComparableDataIdentifier"),
+ choices = ccols,
+ selected = c("TADA.ComparableDataIdentifier"),
multiple = TRUE
)
})
-
+
# runs the summary function when cens button is pushed following group selection
shiny::observeEvent(input$cens_sumbutton, {
- summary <- TADA::TADA_Stats(censdat$dat, group_cols = input$cens_groups)
- censdat$summary <- summary[, !names(summary) %in% c("UpperFence", "LowerFence", "Min", "Max", "Mean", "Percentile_5th", "Percentile_10th", "Percentile_15th", "Percentile_25th", "Percentile_50th_Median", "Percentile_75th", "Percentile_85th", "Percentile_95th", "Percentile_98th")]
+ summary <-
+ TADA::TADA_Stats(censdat$dat, group_cols = input$cens_groups)
+ censdat$summary <-
+ summary[, !names(summary) %in% c(
+ "UpperFence",
+ "LowerFence",
+ "Min",
+ "Max",
+ "Mean",
+ "Percentile_5th",
+ "Percentile_10th",
+ "Percentile_15th",
+ "Percentile_25th",
+ "Percentile_50th_Median",
+ "Percentile_75th",
+ "Percentile_85th",
+ "Percentile_95th",
+ "Percentile_98th"
+ )]
})
-
+
# creates summary table complete with csv button in case someone wants to
# download the summary table
output$cens_sumtable <- DT::renderDT({
- DT::datatable(censdat$summary,
+ DT::datatable(
+ censdat$summary,
extensions = "Buttons",
options = list(
- dom = "Blftipr", scrollX = TRUE,
- pageLength = 10, searching = FALSE,
- order = list(list(length(input$cens_groups), "desc")),
+ dom = "Blftipr",
+ scrollX = TRUE,
+ pageLength = 10,
+ searching = FALSE,
+ order = list(list(length(
+ input$cens_groups
+ ), "desc")),
buttons = c("csv")
),
- selection = "none", rownames = FALSE
+ selection = "none",
+ rownames = FALSE
)
})
})
diff --git a/R/mod_data_flagging.R b/R/mod_data_flagging.R
index 6358f8c34..f7b659cfa 100644
--- a/R/mod_data_flagging.R
+++ b/R/mod_data_flagging.R
@@ -12,34 +12,48 @@
mod_data_flagging_ui <- function(id) {
ns <- NS(id)
tagList(
- tags$div(
- style = "display: none;",
- shinyWidgets::prettySwitch("dummy", label = NULL)
- ),
+ tags$div(style = "display: none;",
+ shinyWidgets::prettySwitch("dummy", label = NULL)),
htmltools::h3("Flag data for potential issues"),
- htmltools::HTML("Click the button below to run a series of tests that check for quality control issues or data formats not compatible with TADA. When the tests are finished running, a table will appear below. Each row describes an evaluation test, reports the number of results affected, and contains a switch users may toggle on/off to decide whether to flag results for removal. However, evaluation tests marked as Required have permanently 'ON' light blue switches that cannot be changed. Recommended tests are automatically switched 'ON' (darker blue), and Optional tests are automatically switched 'OFF' (gray)."),
+ htmltools::HTML(
+ "Click the button below to run a series of tests that check for quality control issues or data formats not compatible with TADA. When the tests are finished running, a table will appear below. Each row describes an evaluation test, reports the number of results affected, and contains a switch users may toggle on/off to decide whether to flag results for removal. However, evaluation tests marked as Required have permanently 'ON' light blue switches that cannot be changed. Recommended tests are automatically switched 'ON' (darker blue), and Optional tests are automatically switched 'OFF' (gray)."
+ ),
htmltools::div(style = "margin-bottom:10px"),
shiny::fluidRow(column(
3,
shiny::actionButton(ns("runFlags"),
- "Run Tests",
- style = "color: #fff; background-color: #337ab7; border-color: #2e6da4"
- )
+ "Run Tests",
+ style = "color: #fff; background-color: #337ab7; border-color: #2e6da4")
)),
htmltools::div(style = "margin-bottom:10px"),
DT::DTOutput(ns("flagTable")),
htmltools::br(),
htmltools::h3("Convert depth units (Optional)"),
- htmltools::HTML("Depth units in the dataset are automatically converted to meters upon data retrieval. Click the radio buttons below to convert depth units to feet, inches, or back to meters."),
- shiny::fluidRow(column(6, shiny::radioButtons(ns("m2f"), label = "", choices = c("feet", "inches", "meters"), selected = character(0), inline = TRUE)))
+ htmltools::HTML(
+ "Depth units in the dataset are automatically converted to meters upon data retrieval. Click the radio buttons below to convert depth units to feet, inches, or back to meters."
+ ),
+ shiny::fluidRow(column(
+ 6,
+ shiny::radioButtons(
+ ns("m2f"),
+ label = "",
+ choices = c("feet", "inches", "meters"),
+ selected = character(0),
+ inline = TRUE
+ )
+ ))
)
}
mod_data_flagging_server <- function(id, tadat) {
shiny::moduleServer(id, function(input, output, session) {
ns <- session$ns
-
flags <- shiny::reactiveValues()
+ values <- shiny::reactiveValues()
+ values$n_fails <- integer(length(n_switches))
+ tadat$selected_flags <- character()
+ tadat$switch_defaults <- prompt_table$Level != "Optional"
+ switch_disabled <- prompt_table$Level == "Required"
flagSwitch <- function(len) {
inputs <- character(len)
@@ -50,7 +64,7 @@ mod_data_flagging_server <- function(id, tadat) {
shinyWidgets::prettySwitch(
ns(switch_name),
label = NULL,
- value = switch_defaults[i],
+ value = tadat$switch_defaults[i],
status = "primary",
fill = TRUE
)
@@ -61,7 +75,7 @@ mod_data_flagging_server <- function(id, tadat) {
}
inputs
}
-
+
shinyValue <- function(id, len) {
unlist(lapply(seq_len(len), function(i) {
value <- input[[paste0(id, i)]]
@@ -72,13 +86,41 @@ mod_data_flagging_server <- function(id, tadat) {
}
}))
}
-
- # Create a separate column in the raw data to indicate whether records
- # were excluded during the first step
- values <- shiny::reactiveValues()
- values$n_fails <- integer(length(n_switches))
- values$selected_flags <- character()
-
+
+
+
+
+ # Runs whenever selected flags are changed
+ shiny::observeEvent(tadat$selected_flags, {
+ prefix = "Flag: "
+ if (!is.null(tadat$removals)) {
+ tadat$removals = dplyr::select(tadat$removals,-(dplyr::starts_with(prefix)))
+ }
+ # Loop through the flags
+ for (flag in tadat$selected_flags) {
+ # If not all the values are NA, add the test results to removals
+ if (!is.null(tadat$removals)) {
+ if (!all(is.na(values$testResults[flag]))) {
+ tadat$removals[paste0(prefix, flag)] = values$testResults[flag]
+ }
+ }
+ # If the switch corresponding to this flag isn't on, switch it on
+ # Checking a random switch to make sure they've been initialized
+ pos = match(flag, prompts)
+ tadat$switch_defaults[pos] = TRUE
+ if (!is.null(input[[paste0("switch_", pos)]])) {
+ switch_name = paste0("switch_", pos)
+ if (is.na(pos)) {
+ invalidFile("flagging")
+ } else if (!isTRUE(input[[switch_name]])) {
+ # Turn the switch on if it isn't already
+ shinyWidgets::updatePrettySwitch(inputId = switch_name,
+ value = TRUE)
+ }
+ }
+ }
+ })
+
# Runs when the flag button is clicked
shiny::observeEvent(input$runFlags, {
shinybusy::show_modal_spinner(
@@ -88,40 +130,30 @@ mod_data_flagging_server <- function(id, tadat) {
session = shiny::getDefaultReactiveDomain()
)
- # Add flagging columns to raw table
+ # Add flagging columns to raw table, make sure line below is
+ # not commented out once done with testing
tadat$raw <- applyFlags(tadat$raw, tadat$orgs)
# write.csv(tadat$raw, "flagged.csv")
# tadat$raw = utils::read.csv("flagged.csv") # THIS IS TRIPS WORKING FILE FOR TESTING, COMMENT OUT WHEN COMMITTING TO DEVELOP
-
+
# A table (raw rows, flags) indicating whether each record passes each test
values$testResults <- flagCensus(tadat$raw)
-
+
# The number of records failing each test
values$n_fails <- colSums(values$testResults)
-
+
# Remove progress bar and display instructions
shinybusy::remove_modal_spinner(session = shiny::getDefaultReactiveDomain())
-
-
+
# Runs when any of the flag switches are changed
shiny::observe({
- switch_id <- "switch_"
- values$selected_flags <- flag_types[shinyValue(switch_id, n_switches)]
+ switch_id = "switch_"
+ tadat$selected_flags = flag_types[shinyValue(switch_id, n_switches)]
for (i in which(switch_disabled)) {
shinyjs::disable(paste0(switch_id, i))
}
})
-
- shiny::observeEvent(values$selected_flags, {
- prefix <- "Flag: "
- tadat$removals <- dplyr::select(tadat$removals, -(dplyr::starts_with(prefix)))
- for (flag in values$selected_flags) {
- if (!all(is.na(values$testResults[flag]))) {
- tadat$removals[paste0(prefix, flag)] <- values$testResults[flag]
- }
- }
- })
-
+
switchTable <- shiny::reactive({
df <- data.frame(
Reason = prompts,
@@ -130,7 +162,7 @@ mod_data_flagging_server <- function(id, tadat) {
Decision = flagSwitch(n_switches)
)
})
-
+
output$flagTable <- DT::renderDT(
shiny::isolate(switchTable()),
escape = FALSE,
@@ -160,8 +192,13 @@ mod_data_flagging_server <- function(id, tadat) {
shinyjs::enable(selector = '.nav li a[data-value="Figures"]')
shinyjs::enable(selector = '.nav li a[data-value="Review"]')
})
-
+
+ shiny::observeEvent(tadat$m2f, {
+ shiny::updateRadioButtons(session, "m2f", selected = tadat$m2f)
+ })
+
shiny::observeEvent(input$m2f, {
+ tadat$m2f <- input$m2f
shiny::req(tadat$raw)
if (input$m2f == "feet") {
shinybusy::show_modal_spinner(
@@ -170,7 +207,8 @@ mod_data_flagging_server <- function(id, tadat) {
text = "Converting depth units to feet...",
session = shiny::getDefaultReactiveDomain()
)
- tadat$raw <- TADA::TADA_ConvertDepthUnits(tadat$raw, unit = "ft")
+ tadat$raw <-
+ TADA::TADA_ConvertDepthUnits(tadat$raw, unit = "ft")
}
if (input$m2f == "inches") {
shinybusy::show_modal_spinner(
@@ -179,7 +217,8 @@ mod_data_flagging_server <- function(id, tadat) {
text = "Converting depth units to inches...",
session = shiny::getDefaultReactiveDomain()
)
- tadat$raw <- TADA::TADA_ConvertDepthUnits(tadat$raw, unit = "in")
+ tadat$raw <-
+ TADA::TADA_ConvertDepthUnits(tadat$raw, unit = "in")
}
if (input$m2f == "meters") {
shinybusy::show_modal_spinner(
@@ -188,7 +227,8 @@ mod_data_flagging_server <- function(id, tadat) {
text = "Converting depth units to meters...",
session = shiny::getDefaultReactiveDomain()
)
- tadat$raw <- TADA::TADA_ConvertDepthUnits(tadat$raw, unit = "m")
+ tadat$raw <-
+ TADA::TADA_ConvertDepthUnits(tadat$raw, unit = "m")
}
shinybusy::remove_modal_spinner(session = shiny::getDefaultReactiveDomain())
})
diff --git a/R/mod_filtering.R b/R/mod_filtering.R
index e308c1ca2..51d0b87aa 100644
--- a/R/mod_filtering.R
+++ b/R/mod_filtering.R
@@ -14,15 +14,13 @@ mod_filtering_ui <- function(id) {
column(
3,
shiny::actionButton(ns("addOnlys"), "Include Only Selected Values",
- style = "color: #fff; background-color: #337ab7; border-color: #2e6da4"
- )
+ style = "color: #fff; background-color: #337ab7; border-color: #2e6da4")
),
column(
3,
shiny::actionButton(ns("addExcludes"),
- "Exclude Selected Values",
- style = "color: #fff; background-color: #337ab7; border-color: #2e6da4"
- )
+ "Exclude Selected Values",
+ style = "color: #fff; background-color: #337ab7; border-color: #2e6da4")
)
),
htmltools::br(),
@@ -37,16 +35,14 @@ mod_filtering_ui <- function(id) {
column(
3,
shiny::actionButton(ns("removeFilters"),
- "Reset Selected Filters",
- style = "color: #fff; background-color: #337ab7; border-color: #2e6da4"
- )
+ "Reset Selected Filters",
+ style = "color: #fff; background-color: #337ab7; border-color: #2e6da4")
),
column(
3,
shiny::actionButton(ns("resetFilters"),
- "Reset All Filters",
- style = "color: #fff; background-color: #337ab7; border-color: #2e6da4"
- )
+ "Reset All Filters",
+ style = "color: #fff; background-color: #337ab7; border-color: #2e6da4")
)
)
)
@@ -63,17 +59,19 @@ mod_filtering_server <- function(id, tadat) {
values$selected_field <- NULL
shinyjs::hide("addOnlys")
shinyjs::hide("addExcludes")
-
+
# make sure dataset being used to create filters is only REMOVE = FALSE
shiny::observe({
shiny::req(tadat$tab)
if (tadat$tab == "Filter") {
# only show unique values from data that have not been flagged for removal
- tables$dat <- subset(tadat$raw, tadat$raw$TADA.Remove == FALSE)
- tables$filter_fields <- TADA::TADA_FieldCounts(tables$dat, display = "key")
+ tables$dat <-
+ subset(tadat$raw, tadat$raw$TADA.Remove == FALSE)
+ tables$filter_fields <-
+ TADA::TADA_FieldCounts(tables$dat, display = "key")
}
})
-
+
# First data table with key columns
output$filterStep1 <- DT::renderDT(
tables$filter_fields,
@@ -86,13 +84,15 @@ mod_filtering_server <- function(id, tadat) {
paging = FALSE
)
)
-
+
# When key column selected, get unique values for that column
shiny::observeEvent(input$filterStep1_rows_selected, {
# Get the name of the selected field
- values$selected_field <- tables$filter_fields[input$filterStep1_rows_selected, ]$Field
+ values$selected_field <-
+ tables$filter_fields[input$filterStep1_rows_selected,]$Field
applyLocks()
- tables$filter_values <- data.frame(getValues(tables$dat, values$selected_field))
+ tables$filter_values <-
+ data.frame(getValues(tables$dat, values$selected_field))
output$promptStep2 <- shiny::renderUI(HTML(
paste0(
"