# app.R # ---- Robust package setup ---- options(repos = c(CRAN = "https://packagemanager.posit.co/cran/latest")) required_packages <- c( "shiny", "bslib", "dplyr", "ggplot2", "plotly", "DT", "htmlwidgets", "pharmaverseadamjnj" ) installed_packages <- rownames(installed.packages()) missing_packages <- setdiff(required_packages, installed_packages) if (length(missing_packages) > 0) { install.packages( missing_packages, type = "binary", dependencies = TRUE ) } # ---- Load packages only after all checks/installations are complete ---- library(shiny) library(bslib) library(dplyr) library(ggplot2) library(plotly) library(DT) library(htmlwidgets) library(pharmaverseadamjnj) # ---- Data preparation ---- adsl <- pharmaverseadamjnj::adsl adsl2 <- adsl %>% select(USUBJID, TRT01P, TRTDURD, WEIGHTBL, HEIGHTBL) %>% filter( !is.na(TRT01P), !is.na(TRTDURD), !is.na(WEIGHTBL), !is.na(HEIGHTBL) ) adsl3 <- adsl2 %>% mutate_if(is.character, as.factor) tf_colors <- c( "Placebo" = "#0072B2", "Xanomeline High Dose" = "#D55E00", "Xanomeline Low Dose" = "#CC79A7" ) duration_range <- range(adsl3$TRTDURD, na.rm = TRUE) weight_range <- range(adsl3$WEIGHTBL, na.rm = TRUE) height_range <- range(adsl3$HEIGHTBL, na.rm = TRUE) # ---- UI ---- ui <- fluidPage( theme = bs_theme(bootswatch = "cosmo"), titlePanel("Quarto Dashboard"), sidebarLayout( sidebarPanel( selectInput( inputId = "treatment_filter", label = "Treatment Group", choices = levels(adsl3$TRT01P), selected = levels(adsl3$TRT01P), multiple = TRUE ), selectInput( inputId = "subject_filter", label = "Subject", choices = levels(adsl3$USUBJID), selected = levels(adsl3$USUBJID), multiple = TRUE ), sliderInput( inputId = "duration_filter", label = "Total Treatment Duration (Days)", min = floor(duration_range[1]), max = ceiling(duration_range[2]), value = c(floor(duration_range[1]), ceiling(duration_range[2])), step = 1 ), sliderInput( inputId = "weight_filter", label = "Baseline Weight (kg)", min = floor(weight_range[1]), max = ceiling(weight_range[2]), value = c(floor(weight_range[1]), ceiling(weight_range[2])), step = 1 ), sliderInput( inputId = "height_filter", label = "Baseline Height (cm)", min = floor(height_range[1]), max = ceiling(height_range[2]), value = c(floor(height_range[1]), ceiling(height_range[2])), step = 1 ) ), mainPanel( fluidRow( column( width = 6, h4("Treatment Duration (Days) by Treatment Group"), plotlyOutput("duration_plot", height = "420px") ), column( width = 6, h4("Baseline Weight (kg) vs Baseline Height (cm)"), plotlyOutput("height_weight_plot", height = "420px") ) ), fluidRow( column( width = 12, h4("Subject-Level Data"), DTOutput("subject_table") ) ) ) ) ) # ---- Server ---- server <- function(input, output, session) { filtered_data <- reactive({ adsl3 %>% filter( TRT01P %in% input$treatment_filter, USUBJID %in% input$subject_filter, TRTDURD >= input$duration_filter[1], TRTDURD <= input$duration_filter[2], WEIGHTBL >= input$weight_filter[1], WEIGHTBL <= input$weight_filter[2], HEIGHTBL >= input$height_filter[1], HEIGHTBL <= input$height_filter[2] ) }) output$duration_plot <- renderPlotly({ f1 <- ggplot( data = filtered_data(), aes(x = TRT01P, y = TRTDURD, fill = TRT01P) ) + geom_boxplot() + scale_x_discrete(name = "Treatment Group") + scale_y_continuous( limits = c(0, 250), breaks = seq(0, 250, 50), expand = c(0.05, 0.05), name = "Treatment Duration (Days)" ) + scale_fill_manual( values = tf_colors, name = "Treatment Group" ) + theme_bw() + theme(legend.position = "top") ggplotly(f1) %>% layout( legend = list( orientation = "h", x = 0.5, xanchor = "center", y = 1.02, yanchor = "bottom" ), margin = list(t = 45) ) }) output$height_weight_plot <- renderPlotly({ f2 <- ggplot( data = filtered_data(), aes( x = HEIGHTBL, y = WEIGHTBL, shape = TRT01P, colour = TRT01P ) ) + geom_point() + scale_x_continuous( limits = c(0, 150), breaks = seq(0, 150, 10), name = "Baseline Height (cm)" ) + scale_y_continuous( limits = c(0, 150), breaks = seq(0, 150, 10), name = "Baseline Weight (kg)" ) + scale_shape_manual( values = c(16, 17, 15), name = "Treatment Group" ) + scale_colour_manual( values = tf_colors, name = "Treatment Group" ) + theme_bw() + theme(legend.position = "top") ggplotly(f2) %>% layout( legend = list( orientation = "h", x = 0.5, xanchor = "center", y = 1.02, yanchor = "bottom" ), margin = list(t = 45) ) }) output$subject_table <- renderDT({ my_table_2 <- filtered_data() %>% rename( "Unique Subject Identifier" = USUBJID, "Treatment Group" = TRT01P, "Total Treatment Duration (Days)" = TRTDURD, "Baseline Weight (kg)" = WEIGHTBL, "Baseline Height (cm)" = HEIGHTBL ) datatable( my_table_2, extensions = c("ColReorder"), rownames = FALSE, filter = "top", class = "stripe hover compact", fillContainer = FALSE, autoHideNavigation = FALSE, options = list( colReorder = TRUE, bPaginate = TRUE, paging = TRUE, lengthChange = TRUE, searching = TRUE, info = TRUE, pageLength = 5, lengthMenu = c(5, 10, 12, 20, 50, 100), dom = '<"top"lf>rt<"bottom"ip>', language = list( info = "Showing _START_ to _END_ of _TOTAL_ entries" ) ) ) }) } # ---- Run app ---- shinyApp(ui = ui, server = server)