2024-11-08 16:31:33 +01:00
|
|
|
|
|
|
|
library(readr)
|
|
|
|
library(MASS)
|
|
|
|
library(stats)
|
|
|
|
library(gtsummary)
|
2024-11-21 12:34:27 +01:00
|
|
|
library(gt)
|
2024-11-08 16:31:33 +01:00
|
|
|
library(openxlsx2)
|
|
|
|
library(haven)
|
|
|
|
library(readODS)
|
|
|
|
library(shiny)
|
|
|
|
library(bslib)
|
|
|
|
library(assertthat)
|
|
|
|
library(dplyr)
|
|
|
|
library(quarto)
|
|
|
|
library(here)
|
2024-11-08 16:47:41 +01:00
|
|
|
library(broom)
|
|
|
|
library(broom.helpers)
|
2024-12-19 15:26:23 +01:00
|
|
|
# library(REDCapCAST)
|
2024-11-29 14:30:02 +01:00
|
|
|
library(easystats)
|
|
|
|
library(patchwork)
|
2024-12-04 12:58:55 +01:00
|
|
|
library(DHARMa)
|
2024-11-27 10:58:31 +01:00
|
|
|
# if (!requireNamespace("webResearch")) {
|
|
|
|
# devtools::install_github("agdamsbo/webResearch", quiet = TRUE, upgrade = "never")
|
|
|
|
# }
|
|
|
|
# library(webResearch)
|
|
|
|
|
|
|
|
if (file.exists(here::here("functions.R"))) {
|
|
|
|
source(here::here("functions.R"))
|
2024-11-21 12:34:27 +01:00
|
|
|
}
|
2024-11-08 15:13:33 +01:00
|
|
|
|
2024-12-19 15:26:23 +01:00
|
|
|
#' freesearcheR server
|
|
|
|
#'
|
|
|
|
#' @param input input
|
|
|
|
#' @param output output
|
|
|
|
#' @param session session
|
|
|
|
#'
|
|
|
|
#' @returns server
|
|
|
|
#' @export
|
|
|
|
#' @importFrom REDCapCAST numchar2fct
|
|
|
|
#'
|
|
|
|
#' @examples
|
2024-11-08 15:13:33 +01:00
|
|
|
server <- function(input, output, session) {
|
2024-11-22 10:53:52 +01:00
|
|
|
## Listing files in www in session start to keep when ending and removing
|
|
|
|
## everything else.
|
|
|
|
files.to.keep <- list.files("www/")
|
|
|
|
|
2024-11-08 15:13:33 +01:00
|
|
|
v <- shiny::reactiveValues(
|
|
|
|
list = NULL,
|
2024-11-21 12:34:27 +01:00
|
|
|
ds = NULL,
|
|
|
|
input = exists("webResearch_data"),
|
2024-11-22 10:53:52 +01:00
|
|
|
local_temp = NULL,
|
2024-11-22 11:48:08 +01:00
|
|
|
quarto = NULL,
|
|
|
|
test = "no"
|
2024-11-08 15:13:33 +01:00
|
|
|
)
|
|
|
|
|
2024-11-22 11:48:08 +01:00
|
|
|
test_data <- shiny::eventReactive(input$test_data, {
|
|
|
|
v$test <- "test"
|
|
|
|
})
|
|
|
|
|
2024-11-08 15:13:33 +01:00
|
|
|
ds <- shiny::reactive({
|
|
|
|
# input$file1 will be NULL initially. After the user selects
|
|
|
|
# and uploads a file, head of that data file by default,
|
|
|
|
# or all rows if selected, will be shown.
|
2024-11-21 12:34:27 +01:00
|
|
|
if (v$input) {
|
|
|
|
out <- webResearch_data
|
2024-11-28 21:02:23 +01:00
|
|
|
} else if (v$test == "test") {
|
2024-11-22 11:48:08 +01:00
|
|
|
out <- gtsummary::trial
|
2024-11-21 12:34:27 +01:00
|
|
|
} else {
|
|
|
|
shiny::req(input$file)
|
|
|
|
out <- read_input(input$file$datapath)
|
|
|
|
}
|
2024-11-22 11:48:08 +01:00
|
|
|
|
2024-11-08 15:13:33 +01:00
|
|
|
v$ds <- "present"
|
2024-11-28 21:02:23 +01:00
|
|
|
if (input$factorize == "yes") {
|
|
|
|
out <- out |>
|
|
|
|
(\(.x){
|
|
|
|
suppressWarnings(
|
2024-12-19 15:26:23 +01:00
|
|
|
numchar2fct(.x)
|
2024-11-28 21:02:23 +01:00
|
|
|
)
|
|
|
|
})()
|
|
|
|
}
|
2024-11-21 12:34:27 +01:00
|
|
|
return(out)
|
2024-11-08 15:13:33 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
output$include_vars <- shiny::renderUI({
|
|
|
|
selectizeInput(
|
|
|
|
inputId = "include_vars",
|
|
|
|
selected = NULL,
|
|
|
|
label = "Covariables to include",
|
2024-11-28 21:02:23 +01:00
|
|
|
choices = colnames(ds()),
|
2024-11-08 15:13:33 +01:00
|
|
|
multiple = TRUE
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
output$outcome_var <- shiny::renderUI({
|
|
|
|
selectInput(
|
|
|
|
inputId = "outcome_var",
|
|
|
|
selected = NULL,
|
|
|
|
label = "Select outcome variable",
|
|
|
|
choices = colnames(ds()),
|
|
|
|
multiple = FALSE
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2024-11-27 11:55:26 +01:00
|
|
|
output$strat_var <- shiny::renderUI({
|
|
|
|
selectInput(
|
|
|
|
inputId = "strat_var",
|
|
|
|
selected = "none",
|
|
|
|
label = "Select variable to stratify baseline",
|
2024-11-28 21:02:23 +01:00
|
|
|
choices = c("none", colnames(ds()[base_vars()])),
|
2024-11-27 11:55:26 +01:00
|
|
|
multiple = FALSE
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2024-11-22 11:48:08 +01:00
|
|
|
output$factor_vars <- shiny::renderUI({
|
|
|
|
selectizeInput(
|
|
|
|
inputId = "factor_vars",
|
|
|
|
selected = colnames(ds())[sapply(ds(), is.factor)],
|
|
|
|
label = "Covariables to format as categorical",
|
2024-11-28 21:02:23 +01:00
|
|
|
choices = colnames(ds()),
|
2024-11-22 11:48:08 +01:00
|
|
|
multiple = TRUE
|
|
|
|
)
|
|
|
|
})
|
|
|
|
|
2024-11-28 21:02:23 +01:00
|
|
|
base_vars <- shiny::reactive({
|
|
|
|
if (is.null(input$include_vars)) {
|
|
|
|
out <- colnames(ds())
|
|
|
|
} else {
|
|
|
|
out <- unique(c(input$include_vars, input$outcome_var))
|
|
|
|
}
|
|
|
|
return(out)
|
2024-11-22 11:48:08 +01:00
|
|
|
})
|
|
|
|
|
2024-12-04 12:58:55 +01:00
|
|
|
output$data.input <-
|
|
|
|
DT::renderDT({
|
|
|
|
shiny::req(input$file)
|
|
|
|
ds()[base_vars()]
|
|
|
|
})
|
2024-11-28 21:02:23 +01:00
|
|
|
|
|
|
|
output$data.classes <- gt::render_gt({
|
2024-11-22 11:48:08 +01:00
|
|
|
shiny::req(input$file)
|
2024-11-28 21:02:23 +01:00
|
|
|
data.frame(matrix(sapply(ds(), \(.x){
|
|
|
|
class(.x)[1]
|
|
|
|
}), nrow = 1)) |>
|
|
|
|
stats::setNames(names(ds())) |>
|
|
|
|
gt::gt()
|
2024-11-08 15:13:33 +01:00
|
|
|
})
|
|
|
|
|
2024-11-28 21:02:23 +01:00
|
|
|
|
|
|
|
|
2024-11-08 15:13:33 +01:00
|
|
|
shiny::observeEvent(
|
|
|
|
{
|
|
|
|
input$load
|
|
|
|
},
|
|
|
|
{
|
|
|
|
shiny::req(input$outcome_var)
|
|
|
|
|
2024-11-15 22:22:17 +01:00
|
|
|
# Assumes all character variables can be formatted as factors
|
|
|
|
data <- ds() |>
|
|
|
|
dplyr::mutate(dplyr::across(dplyr::where(is.character), as.factor))
|
|
|
|
|
2024-11-22 11:48:08 +01:00
|
|
|
data <- data |> factorize(vars = input$factor_vars)
|
|
|
|
|
2024-11-28 21:02:23 +01:00
|
|
|
# if (is.factor(data[[input$strat_var]])) {
|
|
|
|
# by.var <- input$strat_var
|
|
|
|
# } else {
|
|
|
|
# by.var <- NULL
|
|
|
|
# }
|
2024-11-15 22:22:17 +01:00
|
|
|
|
2024-11-28 21:02:23 +01:00
|
|
|
if (input$strat_var == "none") {
|
|
|
|
by.var <- NULL
|
2024-11-21 12:34:27 +01:00
|
|
|
} else {
|
2024-11-28 21:02:23 +01:00
|
|
|
by.var <- input$strat_var
|
2024-11-21 12:34:27 +01:00
|
|
|
}
|
|
|
|
|
2024-11-28 21:02:23 +01:00
|
|
|
data <- data[base_vars()]
|
|
|
|
|
|
|
|
# model <- data |>
|
|
|
|
# regression_model(
|
|
|
|
# outcome.str = input$outcome_var,
|
|
|
|
# auto.mode = input$regression_auto == 1,
|
|
|
|
# formula.str = input$regression_formula,
|
|
|
|
# fun = input$regression_fun,
|
|
|
|
# args.list = eval(parse(text = paste0("list(", input$regression_args, ")")))
|
|
|
|
# )
|
|
|
|
|
|
|
|
models <- list(
|
|
|
|
"Univariable" = regression_model_uv,
|
|
|
|
"Multivariable" = regression_model
|
|
|
|
) |>
|
|
|
|
lapply(\(.fun){
|
|
|
|
do.call(
|
|
|
|
.fun,
|
|
|
|
c(
|
|
|
|
list(data = data),
|
|
|
|
list(outcome.str = input$outcome_var),
|
|
|
|
list(formula.str = input$regression_formula),
|
|
|
|
list(fun = input$regression_fun),
|
|
|
|
list(args.list = eval(parse(text = paste0("list(", input$regression_args, ")"))))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
})
|
2024-11-22 10:53:52 +01:00
|
|
|
|
2024-11-29 14:30:02 +01:00
|
|
|
# browser()
|
|
|
|
# check <- performance::check_model(purrr::pluck(models,"Multivariable") |>
|
|
|
|
# (\(x){
|
|
|
|
# class(x) <- class(x)[class(x) != "webresearch_model"]
|
|
|
|
# return(x)
|
|
|
|
# })())
|
|
|
|
|
|
|
|
check <- purrr::pluck(models, "Multivariable") |>
|
|
|
|
performance::check_model()
|
|
|
|
|
2024-11-21 12:34:27 +01:00
|
|
|
|
2024-11-15 22:22:17 +01:00
|
|
|
v$list <- list(
|
2024-11-21 12:34:27 +01:00
|
|
|
data = data,
|
2024-11-29 14:30:02 +01:00
|
|
|
check = check,
|
2024-11-21 12:34:27 +01:00
|
|
|
table1 = data |>
|
|
|
|
baseline_table(
|
|
|
|
fun.args =
|
|
|
|
list(
|
|
|
|
by = by.var
|
|
|
|
)
|
2024-11-27 11:55:26 +01:00
|
|
|
) |>
|
|
|
|
(\(.x){
|
2024-11-28 21:02:23 +01:00
|
|
|
if (!is.null(by.var)) {
|
2024-11-27 11:55:26 +01:00
|
|
|
.x |> gtsummary::add_overall()
|
|
|
|
} else {
|
|
|
|
.x
|
|
|
|
}
|
2024-11-28 21:02:23 +01:00
|
|
|
})() |>
|
|
|
|
(\(.x){
|
|
|
|
if (input$add_p == "yes") {
|
|
|
|
.x |>
|
|
|
|
gtsummary::add_p() |>
|
|
|
|
gtsummary::bold_p()
|
|
|
|
} else {
|
|
|
|
.x
|
|
|
|
}
|
2024-11-27 11:55:26 +01:00
|
|
|
})(),
|
2024-11-28 21:02:23 +01:00
|
|
|
table2 = models |>
|
|
|
|
purrr::map(regression_table) |>
|
2024-12-04 12:58:55 +01:00
|
|
|
tbl_merge(),
|
|
|
|
input = input
|
2024-11-21 12:34:27 +01:00
|
|
|
)
|
2024-11-08 15:13:33 +01:00
|
|
|
|
|
|
|
output$table1 <- gt::render_gt(
|
|
|
|
v$list$table1 |>
|
|
|
|
gtsummary::as_gt()
|
|
|
|
)
|
|
|
|
|
|
|
|
output$table2 <- gt::render_gt(
|
|
|
|
v$list$table2 |>
|
|
|
|
gtsummary::as_gt()
|
|
|
|
)
|
2024-11-29 14:30:02 +01:00
|
|
|
|
|
|
|
output$check <- shiny::renderPlot({
|
|
|
|
p <- plot(check) +
|
|
|
|
patchwork::plot_annotation(title = "Multivariable regression model checks")
|
|
|
|
p
|
|
|
|
# Generate checks in one column
|
|
|
|
# layout <- sapply(seq_len(length(p)), \(.x){
|
|
|
|
# patchwork::area(.x, 1)
|
|
|
|
# })
|
|
|
|
#
|
|
|
|
# p + patchwork::plot_layout(design = Reduce(c, layout))
|
|
|
|
|
|
|
|
# patchwork::wrap_plots(ncol=1) +
|
|
|
|
# patchwork::plot_annotation(title = 'Multivariable regression model checks')
|
|
|
|
})
|
2024-11-08 15:13:33 +01:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2024-11-29 14:30:02 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2024-11-08 15:13:33 +01:00
|
|
|
output$uploaded <- shiny::reactive({
|
|
|
|
if (is.null(v$ds)) {
|
|
|
|
"no"
|
|
|
|
} else {
|
|
|
|
"yes"
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
shiny::outputOptions(output, "uploaded", suspendWhenHidden = FALSE)
|
|
|
|
|
2024-11-21 12:34:27 +01:00
|
|
|
output$has_input <- shiny::reactive({
|
|
|
|
if (v$input) {
|
|
|
|
"yes"
|
|
|
|
} else {
|
|
|
|
"no"
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
shiny::outputOptions(output, "has_input", suspendWhenHidden = FALSE)
|
|
|
|
|
|
|
|
# Could be rendered with other tables or should show progress
|
|
|
|
# Investigate quarto render problems
|
|
|
|
# On temp file handling: https://github.com/quarto-dev/quarto-cli/issues/3992
|
2024-11-08 15:13:33 +01:00
|
|
|
output$report <- downloadHandler(
|
2024-11-22 10:53:52 +01:00
|
|
|
filename = shiny::reactive({
|
|
|
|
paste0("report.", input$output_type)
|
|
|
|
}),
|
|
|
|
content = function(file, type = input$output_type) {
|
2024-12-09 14:00:44 +01:00
|
|
|
## Notification is not progressing
|
|
|
|
## Presumably due to missing
|
|
|
|
shiny::withProgress(message = "Generating report. Hold on for a moment..", {
|
|
|
|
v$list |>
|
|
|
|
write_quarto(
|
|
|
|
output_format = type,
|
|
|
|
input = file.path(getwd(), "www/report.qmd")
|
|
|
|
)
|
|
|
|
})
|
2024-11-22 10:53:52 +01:00
|
|
|
file.rename(paste0("www/report.", type), file)
|
2024-11-08 15:13:33 +01:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2024-11-22 10:53:52 +01:00
|
|
|
session$onSessionEnded(function() {
|
|
|
|
cat("Session Ended\n")
|
|
|
|
files <- list.files("www/")
|
|
|
|
lapply(files[!files %in% files.to.keep], \(.x){
|
|
|
|
unlink(paste0("www/", .x), recursive = FALSE)
|
|
|
|
print(paste(.x, "deleted"))
|
|
|
|
})
|
|
|
|
})
|
2024-11-08 15:13:33 +01:00
|
|
|
}
|