]> nmode's Git Repositories - Rnaught/commitdiff
Remove old Shiny UI and server files
authorNaeem Model <me@nmode.ca>
Thu, 9 May 2024 20:34:40 +0000 (20:34 +0000)
committerNaeem Model <me@nmode.ca>
Thu, 9 May 2024 20:34:40 +0000 (20:34 +0000)
R/server.R [deleted file]
R/ui.R [deleted file]

diff --git a/R/server.R b/R/server.R
deleted file mode 100644 (file)
index a9ed521..0000000
+++ /dev/null
@@ -1,314 +0,0 @@
-#' @importFrom methods is
-#' @importFrom utils read.csv write.csv
-server <- function(input, output) {
-  reactive <- shiny::reactiveValues(
-    data_table = data.frame(Name = character(0),
-                            `Reporting Frequency` = character(0),
-                            `Case Counts` = numeric(0), check.names = FALSE),
-    est_table = data.frame(Dataset = character(0)),
-    estimators = list()
-  )
-
-  # Validate and add datasets when button is clicked.
-  # Also evaluate the new datasets on existing estimators.
-  shiny::observeEvent(input$addData, {
-    # Option 1: Manual entry.
-    if (input$dataInputMethod == 1) {
-      checks_passed <- TRUE
-
-      # Ensure the dataset name is not blank.
-      if (grepl("^\\s*$", input$dataName)) {
-        output$dataNameWarn <- shiny::renderText(
-          "Error: The dataset name cannot be blank.")
-        checks_passed <- FALSE
-      }
-      # Ensure the dataset name is not a duplicate.
-      else if (input$dataName %in% reactive$data_table[,1]) {
-        output$dataNameWarn <- shiny::renderText(
-          "Error: There is already a dataset with the specified name.")
-        checks_passed <- FALSE
-      }
-      else
-        output$dataNameWarn <- shiny::renderText("")
-
-      # Ensure the case counts consist only of positive integers, separated by
-      # commas.
-      counts <- as.numeric(unlist(strsplit(input$dataCounts, split = ",")))
-      if (any(is.na(counts)) || any(counts <= 0) || any(counts %% 1 != 0)) {
-        output$dataCountsWarn <- shiny::renderText("Error: The list of case
-          counts should only contain positive integers, separated by commas.")
-        checks_passed <- FALSE
-      }
-      # Ensure the case counts contain at least two entries.
-      else if (length(counts) < 2) {
-        output$dataCountsWarn <- shiny::renderText(
-          "Error: The list of case counts should contain at least two entries.")
-        checks_passed <- FALSE
-      }
-      else
-        output$dataCountsWarn <- shiny::renderText("")
-      
-      if (checks_passed)
-        d <- data.frame(input$dataName, input$dataUnits, t(counts))
-    }
-
-    else {
-      checks_passed <- FALSE
-
-      # Option 2: Upload .csv
-      if (input$dataInputMethod == 2)
-        d <- try(read.csv(input$dataUpload$datapath, header = FALSE))
-      # Option 3: Paste .csv
-      else
-        d <- try(read.csv(text = input$dataPaste, header = FALSE))
-
-      if (is(d, "try-error"))
-        output$dataCSVWarn <- shiny::renderText("Error reading file.")
-      else if (ncol(d) < 4 || anyNA(d[,1]) || anyNA(sapply(d[,3:4], as.numeric))
-               || !all(trimws(d[,2]) %in% c("Daily", "Weekly")))
-        output$dataCSVWarn <- shiny::renderText(
-          "Error: The provided .csv file does not match the required format.")
-      else if (length(intersect(reactive$data_table[,1], d[,1])) > 0)
-        output$dataCSVWarn <- shiny::renderText("Error: The provided .csv file
-          contains dataset names which already exist.")
-      else if (length(unique(d[,1])) != length(d[,1]))
-        output$dataCSVWarn <- shiny::renderText(
-          "Error: The provided .csv file contains duplicate dataset names.")
-      else {
-        output$dataCSVWarn <- shiny::renderText("")
-        checks_passed <- TRUE
-      }
-    }
-
-    if (checks_passed) {
-      d[,3:ncol(d)] <- apply(d[,3:ncol(d)], 2, as.numeric)
-      d[,3] <- data.frame(I(lapply(split(d[,3:ncol(d)], 1:nrow(d)),
-                                   function(x) x[!is.na(x)])))
-      d <- d[,1:3]
-      d[,2] <- trimws(d[,2])
-      colnames(d) <- c("Name", "Reporting Frequency", "Case Counts")
-      reactive$data_table <- rbind(reactive$data_table, d)
-      reactive$est_table <- update_est_row(input, output, d,
-        reactive$estimators, reactive$est_table)
-    }
-  })
-  
-  output$dataTable <- shiny::renderDataTable(reactive$data_table,
-                                             escape = FALSE)
-  output$estTable <- shiny::renderDataTable(reactive$est_table,
-                                            escape = FALSE)
-
-  # Download table of estimates as a .csv file.
-  output$downloadEst <- shiny::downloadHandler(
-    filename = function() { paste0("Rnaught-", Sys.Date(), ".csv") },
-    content = function(file) { write.csv(reactive$est_table, file) }
-  )
-
-  shiny::observeEvent(input$addWP, {
-    if (input$serialWPKnown == 1) {
-      serial <- validate_serial(input, output, "serialWPInput", "serialWPWarn")
-      if (!is.na(serial)) {
-        reactive$estimators[[length(reactive$estimators) + 1]] <- list(
-          method = "WP", mu = serial, mu_units = input$serialWPUnits,
-          grid_length = 100, max_shape = 10, max_scale = 10)
-        reactive$est_table <- update_est_col(input, output, reactive$data_table,
-          reactive$estimators[[length(reactive$estimators)]],
-          reactive$est_table)
-      }
-    }
-    else {
-      checks_passed <- TRUE
-
-      grid_length <- as.numeric(input$gridLengthInput)
-      max_shape <- as.numeric(input$gridShapeInput)
-      max_scale <- as.numeric(input$gridScaleInput)
-
-      if (is.na(grid_length) || grid_length <= 0 || grid_length %% 1 != 0) {
-        output$gridLengthWarn <- shiny::renderText(
-          "Error: The grid size must be a positive integer.")
-        output$gridShapeWarn <- shiny::renderText("")
-        output$gridScaleWarn <- shiny::renderText("")
-        checks_passed <- FALSE
-      }
-      else {
-        output$gridLengthWarn <- shiny::renderText("")
-
-        if (is.na(max_shape) || max_shape < 1 / grid_length) {
-          output$gridShapeWarn <- shiny::renderText("Error: The maximum shape
-            must be at least the reciprocal of the grid length.")
-          checks_passed <- FALSE
-        }
-        else
-          output$gridShapeWarn <- shiny::renderText("")
-
-        if (is.na(max_scale) || max_scale < 1 / grid_length) {
-          output$gridScaleWarn <- shiny::renderText("Error: The maximum scale
-            must be at least the reciprocal of the grid length.")
-          checks_passed <- FALSE
-        }
-        else
-          output$gridScaleWarn <- shiny::renderText("")
-      }
-
-      if (checks_passed) {
-        reactive$estimators[[length(reactive$estimators) + 1]] <- list(
-          method = "WP", mu = NA, mu_units = input$serialWPUnits,
-          grid_length = grid_length, max_shape = max_shape, max_scale = max_scale)
-        reactive$est_table <- update_est_col(input, output, reactive$data_table,
-          reactive$estimators[[length(reactive$estimators)]],
-          reactive$est_table)
-      }  
-    }
-  })
-
-  shiny::observeEvent(input$addseqB, {
-    serial <- validate_serial(input, output, "serialseqBInput",
-                              "serialseqBWarn")
-    checks_passed <- !is.na(serial)
-
-    kappa <- as.numeric(input$kappaInput)
-    if (is.na(kappa) || kappa <= 0) {
-      output$kappaWarn <- shiny::renderText(
-        "Error: The maximum value must be a positive number.")
-      checks_passed <- FALSE
-    }
-    else
-      output$kappaWarn <- shiny::renderText("")
-
-    if (checks_passed) {
-      reactive$estimators[[length(reactive$estimators) + 1]] <- list(
-        method="seqB", mu = serial, kappa = kappa,
-        mu_units = input$serialseqBUnits)
-      reactive$est_table <- update_est_col(input, output, reactive$data_table,
-        reactive$estimators[[length(reactive$estimators)]], reactive$est_table)
-    }
-  })
-
-  shiny::observeEvent(input$addID, {
-    serial <- validate_serial(input, output, "serialIDInput", "serialIDWarn")
-    if (!is.na(serial)) {
-      reactive$estimators[[length(reactive$estimators) + 1]] <- list(
-        method = "ID", mu = serial, mu_units = input$serialIDUnits)
-      reactive$est_table <- update_est_col(input, output, reactive$data_table,
-        reactive$estimators[[length(reactive$estimators)]], reactive$est_table)
-    }
-  })
-
-  shiny::observeEvent(input$addIDEA, {
-    serial <- validate_serial(input, output, "serialIDEAInput",
-                              "serialIDEAWarn")
-    if (!is.na(serial)) {
-      reactive$estimators[[length(reactive$estimators) + 1]] <- list(
-        method = "IDEA", mu = serial, mu_units = input$serialIDEAUnits)
-      reactive$est_table <- update_est_col(input, output, reactive$data_table,
-        reactive$estimators[[length(reactive$estimators)]], reactive$est_table)
-    }
-  })
-}
-
-validate_serial <- function(input, output, serialInputId, serialWarnId) {
-  serial <- as.numeric(input[[serialInputId]])
-  if (is.na(serial) || serial <= 0) {
-    output[[serialWarnId]] <- shiny::renderText(
-      "Error: The mean serial interval should be a positive number.")
-    serial <- NA
-  }
-  else
-    output[[serialWarnId]] <- shiny::renderText("") # Clear warning text.
-
-  return(serial)
-}
-
-# Create a new column in the estimator table when a new estimator is added.
-update_est_col <- function(input, output, datasets, estimator, est_table) {
-  if (nrow(datasets) == 0)
-    new_est_table <- data.frame(matrix(nrow = 0, ncol = ncol(est_table) + 1))
-  else {
-    estimates <- rep(NA, nrow(datasets))
-
-    for (row in 1:nrow(datasets))
-      estimates[row] <- eval_estimator(input, output, estimator, datasets[row,])
-
-      if (nrow(est_table) == 0)
-        new_est_table <- cbind(datasets[,1], estimates)
-      else
-        new_est_table <- cbind(est_table, estimates)
-  }
-
-  colnames(new_est_table) <- c(colnames(est_table), shiny::HTML(
-    paste0(estimator$method, "<br/>(&mu; = ", estimator$mu, " ",
-           tolower(estimator$mu_units), ")")))
-
-  return(new_est_table)
-}
-
-# Create a new row in the estimator table when new datasets are added.
-update_est_row <- function(input, output, datasets, estimators, est_table) {
-  if (length(estimators) == 0) {
-    if (nrow(est_table) == 0)
-      new_est_table <- data.frame(datasets[,1])
-    else
-      new_est_table <- data.frame(c(est_table[,1], datasets[,1]))
-
-    colnames(new_est_table) <- colnames(est_table)
-  }
-  else {
-    new_est_table <- data.frame(matrix(nrow = nrow(datasets),
-                                       ncol = length(estimators)))
-
-    for (row in 1:nrow(datasets))
-      for (col in 1:length(estimators))
-        new_est_table[row, col] <- eval_estimator(input, output,
-          estimators[[col]], datasets[row,])
-
-    new_est_table <- cbind(datasets[,1], new_est_table)
-    colnames(new_est_table) <- colnames(est_table)
-    new_est_table <- rbind(est_table, new_est_table)
-  }
-
-  return(new_est_table)
-}
-
-# Evaluate an estimator on a given dataset.
-eval_estimator <- function(input, output, estimator, dataset) {
-  # Adjust serial interval to match time unit of case counts.
-  serial <- estimator$mu
-  if (estimator$mu_units == "Days" && dataset[2] == "Weekly")
-    serial <- serial / 7
-  else if (estimator$mu_units == "Weeks" && dataset[2] == "Daily")
-    serial <- serial * 7
-
-  # White and Pagano
-  if (estimator$method == "WP") {
-    estimate <- wp(unlist(dataset[3]), mu = serial, serial = TRUE,
-                   grid_length = estimator$grid_length,
-                   max_shape = estimator$max_shape,
-                   max_scale = estimator$max_scale)
-
-    if (!is.na(estimator$mu))
-      estimate <- round(estimate$r0, 2)
-    # Display the estimated mean of the serial distribution if mu was not
-    # specified.
-    else {
-      if (dataset[2] == "Daily")
-        mu_units <- "days"
-      else
-        mu_units <- "weeks"
-      MSI <- sum(estimate$supp * estimate$pmf)
-      estimate <- shiny::HTML(paste0(round(estimate$r0, 2), "<br/>(&mu; = ",
-                                     round(MSI, 2), " ", mu_units, ")"))
-    }
-  }
-  # Sequential Bayes
-  else if (estimator$method == "seqB")
-    estimate <- round(seq_bayes(unlist(dataset[3]), mu = serial,
-                           kappa = estimator$kappa), 2)
-  # Incidence Decay
-  else if (estimator$method == "ID")
-    estimate <- round(id(unlist(dataset[3]), mu = serial), 2)
-  # Incidence Decay with Exponential Adjustement
-  else if (estimator$method == "IDEA")
-    estimate <- round(idea(unlist(dataset[3]), mu = serial), 2)
-
-  return(estimate)
-}
diff --git a/R/ui.R b/R/ui.R
deleted file mode 100644 (file)
index 911061a..0000000
--- a/R/ui.R
+++ /dev/null
@@ -1,192 +0,0 @@
-ui <- function() {
-  shiny::fluidPage(
-    # Title.
-    shiny::titlePanel(shiny::HTML(
-      paste0("Rnaught: An Estimation Suite for R", shiny::tags$sub("0")))),
-    # Sidebar layout.
-    shiny::sidebarLayout(
-      # Sidebar. Hidden if the 'About' tab is selected.
-      shiny::conditionalPanel(condition = "input.tabset != 'About'",
-        shiny::sidebarPanel(id = "sidebar", data_sidebar(), est_sidebar())),
-      # Main panel.
-      shiny::mainPanel(id = "main",
-        shiny::tabsetPanel(id = "tabset", type = "tabs",
-          shiny::tabPanel("About", shiny::br(), "Hello"),
-          shiny::tabPanel("Data", shiny::br(),
-            shiny::dataTableOutput(outputId = "dataTable"),
-            shiny::tags$style(type = "text/css",
-                              "#dataTable tfoot { display:none; }")),
-          shiny::tabPanel("Estimators", shiny::br(),
-            shiny::dataTableOutput(outputId = "estTable"),
-            shiny::tags$style(type = "text/css",
-                              "#estTable tfoot { display:none; }"),
-            shiny::downloadButton(outputId = "downloadEst",
-                                  label = "Download table as .csv"))
-        )
-      )
-    )
-  )
-}
-
-# Data tab sidebar.
-data_sidebar <- function() {
-  shiny::conditionalPanel(condition = "input.tabset == 'Data'",
-    shiny::h3("Enter data"),
-    # Data input method selection.
-    shiny::radioButtons(inputId = "dataInputMethod", label = "",
-      choices=list("Manually" = 1, "Upload a .csv file" = 2,
-                   "Paste a .csv file" = 3)),
-    # Option 1: Manual entry.
-    shiny::conditionalPanel(condition = "input.dataInputMethod == '1'",
-      shiny::textInput(inputId = "dataName", label = "Dataset name"),
-      shiny::span(shiny::textOutput(outputId = "dataNameWarn"),
-                  style = "color: red;"),
-      shiny::fluidRow(
-        shiny::column(8,
-          shiny::textInput(inputId = "dataCounts",
-            label = help_tool("Case counts", paste0("Enter as a ",
-              "comma-separated list of positive integers, with at least two ",
-              "entries. (Example: 1,1,2,3,5,8)")))),
-        shiny::column(4, shiny::selectInput(inputId = "dataUnits",
-          label = "Reporting frequency", choices = list("Daily", "Weekly")))
-      ),
-      shiny::span(shiny::textOutput(outputId = "dataCountsWarn"),
-                  style = "color: red;")
-    ),
-    # Option 2: Upload .csv file.
-    shiny::conditionalPanel(condition = "input.dataInputMethod == '2'",
-      shiny::fileInput(inputId = "dataUpload", label = "",
-        accept = c("text/csv", "text/comma-separated-values,text/plain",
-                   ".csv")),
-    ),
-    # Option 3: Paste .csv file.
-    shiny::conditionalPanel(condition = "input.dataInputMethod == '3'",
-      shiny::textAreaInput(inputId = "dataPaste", label = "",
-                           rows = 8, resize = "none"),
-    ),
-    # Warning text for .csv upload / paste.
-    shiny::conditionalPanel(
-      condition = "['2', '3'].includes(input.dataInputMethod)",
-      shiny::span(shiny::textOutput(outputId = "dataCSVWarn"),
-                  style = "color: red;"),
-    ),
-    # Button to add data.
-    shiny::actionButton(inputId = "addData", label = "Add")
-  )
-}
-
-# Estimators tab sidebar.
-est_sidebar <- function() {
-  shiny::conditionalPanel(condition = "input.tabset == 'Estimators'",
-    shiny::h3("Estimators"),
-    WP_collapse(),
-    seqB_collapse(),
-    ID_collapse(),
-    IDEA_collapse(),
-
-    shiny::tags$style(type = "text/css",
-                      "summary { display: list-item; cursor: pointer; }"),
-    shiny::tags$style(type = "text/css", "summary h4 { display: inline; }")
-  )
-}
-
-# Collapsable entry for White & Pagano (WP) method.
-WP_collapse <- function() {
-  shiny::tags$details(
-    shiny::tags$summary(shiny::h4("White & Pagano (WP)")),
-    shiny::p("Method due to White and Pagano (2008), assumes a branching process
-             based model. Serial distribution can be assumed known or can be
-             estimated using maximum likelihood;  When serial interval is
-             unknown the method takes longer to compute, though is still
-             real-time."),
-    shiny::br(),
-    shiny::radioButtons(inputId = "serialWPKnown",
-                        label = "Is the mean serial interval known?",
-                        inline = TRUE, choices = list("Yes" = 1, "No" = 2)),
-    # Known serial interval.
-    shiny::conditionalPanel(condition = "input.serialWPKnown == '1'",
-      serial_fields("WP")),
-    # Unknown serial interval.
-    shiny::conditionalPanel(condition = "input.serialWPKnown == '2'",
-      shiny::h5("Grid Search Parameters"),
-      shiny::fluidRow(
-        shiny::column(4, shiny::textInput(inputId = "gridLengthInput",
-          label = "Grid length", value = "100")),
-        shiny::column(4, shiny::textInput(inputId = "gridShapeInput",
-          label = "Max. shape", value = "10")),
-        shiny::column(4, shiny::textInput(inputId = "gridScaleInput",
-          label = "Max. scale", value = "10"))
-      ),
-      shiny::fluidRow(
-        shiny::column(4, shiny::span(shiny::textOutput(
-          outputId = "gridLengthWarn"), style = "color: red;")),
-        shiny::column(4, shiny::span(shiny::textOutput(
-          outputId = "gridShapeWarn"), style = "color: red;")),
-        shiny::column(4, shiny::span(shiny::textOutput(
-          outputId = "gridScaleWarn"), style = "color: red;"))
-      )
-    ),
-    shiny::actionButton(inputId = "addWP", label = "Add")
-  )
-}
-
-# Collapsable entry for Sequential Bayes (seqB) method.
-seqB_collapse <- function() {
-  shiny::tags$details(
-    shiny::tags$summary(shiny::h4("Sequential Bayes (seqB)")),
-    shiny::p("This is a description of the method."),
-    shiny::br(),
-    serial_fields("seqB"),
-    shiny::textInput(inputId = "kappaInput",
-      label = help_tool("Maximum value", paste0("This describes the prior ",
-        "belief of R0, and should be set to a higher value if R0 is believed ",
-        "be larger. (Default: 20)")), value = "20"),
-    shiny::span(shiny::textOutput(outputId = "kappaWarn"),
-                style = "color: red;"),
-    shiny::actionButton(inputId = "addseqB", label = "Add")
-  )
-}
-
-# Collapsable entry for Incidence Decay (ID) method.
-ID_collapse <- function() {
-  shiny::tags$details(
-    shiny::tags$summary(shiny::h4("Incidence Decay (ID)")),
-    shiny::p("This is a description of the method."),
-    shiny::br(),
-    serial_fields("ID"),
-    shiny::actionButton(inputId = "addID", label = "Add")
-  )
-}
-
-# Collapsable entry for Incidence Decay & Exponential Adjustment (IDEA) method.
-IDEA_collapse <- function() {
-  shiny::tags$details(
-    shiny::tags$summary(
-      shiny::h4("Incidence Decay and Exponential Adjustment (IDEA)")),
-    shiny::p("This is a description of the method."),
-    shiny::br(),
-    serial_fields("IDEA"),
-    shiny::actionButton(inputId = "addIDEA", label = "Add")
-  )
-}
-
-# Input fields and warning text for the mean serial interval.
-serial_fields <- function(method) {
-  shiny::HTML(paste0(
-    shiny::fluidRow(
-      shiny::column(8, shiny::textInput(
-        inputId = paste0("serial", method, "Input"),
-        label = "Mean Serial Interval")),
-      shiny::column(4, shiny::selectInput(
-        inputId = paste0("serial", method, "Units"),
-        label = "Time units", choices = list("Days", "Weeks")))
-    ),
-    shiny::span(shiny::textOutput(outputId = paste0("serial", method, "Warn")),
-                style = "color: red;")
-  ))
-}
-
-# Display help information on hover.
-help_tool <- function(label, help_text) {
-  shiny::HTML(paste0(label, shiny::tags$sup("[?]", title = help_text)))
-}