R/shinyMethods.R

Defines functions compareTwoPhenoShiny moduleEvals

Documented in compareTwoPhenoShiny moduleEvals

#' Shiny Application for Optimal Module Evaluations
#' 
#' @description Shiny application that includes two panels. On the first panel, the user can conduct a step-by-step investigation of the consensus-based module selection, and the results will be summarized into an R command in which the user copies and pastes to the R console. The second panel includes a download window for extracting the module pattern, silhouette plot, consensus, and correlation plots. 
#' 
#' @param C3NAObj (Required) The C3NAObj with a single phenotype and after the initiateC3NA function. 
#' @param colorSeed (Optional) The seed set for reproducible random colors for the bar plot fill. Default: 9.
#' 
#' @importFrom shinydashboard box tabBox
#' @importFrom shinyjs useShinyjs
#' @importFrom tibble as_tibble
#' @importFrom grDevices colorRampPalette
#' @importFrom RColorBrewer brewer.pal
#' @importFrom dplyr case_when 
#' @rawNamespace import(shiny, except = c(renderDataTable, dataTableOutput))
#' @import ggplot2
#' @import pheatmap
#' @importFrom shinyWidgets setShadow actionBttn useShinydashboard awesomeCheckboxGroup updateAwesomeCheckboxGroup radioGroupButtons downloadBttn
#' @export
#' 

moduleEvals = function(C3NAObj = C3NAObj, colorSeed = 9){
  ## Extract the needed data and basic processing
  module_df = C3NAObj$moduleData
  for(minS in unique(module_df$minSize)){
    module_df_temp = module_df %>% dplyr::filter(minSize == minS)
    if(minS == 3){
      moduleStat =
        module_df_temp %>%
        group_by(phenotype, minSize, colors) %>%
        dplyr::summarise(nTaxa = n(), .groups = "drop") %>%
        mutate(totalNTaxa = length(unique(module_df_temp$taxaID)),
               totalUniqueColors = length(unique(colors)))
    }else {
      moduleStat_temp =
        module_df_temp %>%
        group_by(phenotype, minSize, colors) %>%
        dplyr::summarise(nTaxa = n(), .groups = "drop") %>%
        mutate(totalNTaxa = length(unique(module_df_temp$taxaID)),
               totalUniqueColors = length(unique(colors)))
      moduleStat = rbind(moduleStat, moduleStat_temp)
    }
  }
  
  # Check for duplicated patterns
  moduleCheck = moduleStat %>% ungroup() %>%
    dplyr::select(minSize, colors, nTaxa) %>% distinct() %>%
    pivot_wider(names_from = colors, values_from = nTaxa) %>% mutate(minSize = as.numeric(minSize)) %>%
    arrange(minSize)
  moduleCheck2 = moduleCheck %>%
    group_by(across(-c(minSize)), .drop = F) %>% mutate(n = n())
  moduleCheckRemoveDup = moduleCheck %>%
    group_by(across(-c(minSize)), .drop = F) %>% filter(row_number() == 1) %>%
    pull(minSize)

  set.seed(colorSeed)
  colorTable = data.frame(
    colors = unique(moduleStat$colors),
    randomColors = randomcoloR::distinctColorPalette(k = length(unique(moduleStat$colors)))
  )
  oriMaxClusters = max(moduleStat$totalUniqueColors)
  moduleStatV2 = moduleStat %>%
    left_join(colorTable, by = "colors")
  grayScale = moduleStatV2 %>%
    filter(!(minSize %in% moduleCheckRemoveDup)) %>%
    mutate(randomColorsGrayScale = "#000000",
           colors = "Removed")
  maxSizeCluster = moduleStatV2 %>% dplyr::select(minSize, totalUniqueColors) %>% distinct() %>% 
    filter(totalUniqueColors <= 10) %>% pull(minSize)
  
  shinyApp(
    ui = navbarPage(
      title = "C3NA", 
      # theme = bs_theme
      tags$head(
        tags$style(
          HTML(
            ".checkbox-inline { 
                    margin-left: 0px;
                    margin-right: 10px;}
             .checkbox-inline+.checkbox-inline {
                        margin-left: 0px;
                        margin-right: 10px;}
            .shiny-notification {
                  height: 100px;
                  width: 800px;
                  position:fixed;
                  top: calc(50% - 50px);
                  left: calc(50% - 400px);}
            .btn-custom-status-downloadPlots:hover, .btn-custom-status-downloadPlots:active, 
            .btn-custom-status-downloadPlots:focus, .btn-custom-status-downloadPlots:visited, 
            .btn-custom-status-downloadPlots.active, .open .dropdown-toggle.btn-custom-status-downloadPlots { 
              color: #ffffff;
              background-color: #318dc3;
              border-color: #07486f;
              box-shadow: inset 4px 4px 4px 0 #104667,
                          inset -4px -4px 4px 0 #5ebdf7;
            }
            .btn-custom-status-downloadPlots { 
              color: black; 
              background-color: #88bcdc ; 
              border-color: #07486f;
              box-shadow: 4px 4px 4px 0 rgba(0, 0, 0, 0.2),
                          -4px -4px 4px 0 rgba(255, 255, 255, 0.5),
                          inset 3px 3px 3px 0 #bdd5e4,
                          inset -3px -3px 3px 0 #0c3a56;
            } 
            "
          )
        ) 
      ),
      #####  Panel 1 #####
      tabPanel(
        ## First Panel
        title = HTML("<b>Panel 1: Consensus-Based Evaluations</b>"),
        value = "panel1",
        useShinyjs(),
        useShinydashboard(),
        setShadow(class = "box"),
        
        ##### Section 2 Taxa Statistics #####
        ##### Section 2.1 Pattern Plot #####
        fluidRow(
          box(
            width = 12,
            title = HTML("Pattern Visualization", "<font size='3'>", 
                    as.character(actionLink(inputId = "MS_PatternVis", 
                                            label = "", 
                                            icon = icon("question-circle"))), "</font>"), 
            status = "warning",
            solidHeader = TRUE, 
            shiny::plotOutput("uniquePatterns")
          )
        ),        
        ##### Section 2.2 Pattern Selector #####
        fluidRow(
          box(
            width = 6,
            title = HTML("Select the Unique Patterns from Differnt Minimal Number of Taxa per Module", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_UniquePatternSelector", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            status = "danger",
            solidHeader = TRUE, 
            uiOutput("patternSelector"),
            actionBttn(
              inputId = "uniquePatternsConfirm",
              label = "Confirm Selected Patterns",
              style = "unite", 
              color = "danger"
            )
          ),
          box(
            width = 3,
            title = HTML("Select the Optimal Number of Modules", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_OptimalModuleSelector", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            status = "primary",
            solidHeader = TRUE, 
            conditionalPanel(
              condition = "input.uniquePatternsConfirm==0",
              h6("Please Calculate the Silhouette Plot on the Left.")
            ),
            conditionalPanel(
              condition = "input.uniquePatternsConfirm>0",
              h6("Manually adjust the optimal number of modules based on the Silhouette, Consensus and Correlation Plot. "),
              uiOutput("optNModules"),
              actionBttn(
                inputId = "updateClusters",
                label = "Confirm Selected Patterns",
                style = "unite", 
                color = "primary"
              )
            )
          ),
          box(
            width = 3,
            title = HTML("Confirm the Optimal Clusters", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_OptimalModuleCodeGenerator", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            status = "success",
            solidHeader = TRUE, 
            conditionalPanel(
              condition = "input.updateClusters==0",
              h6("Please Confirm the Optimanl Number of Modules on the Left.")
            ),
            conditionalPanel(
              condition = "input.updateClusters>0",
              h6("Please run the following code after confirming the optimal number of modules."),
              h6("Make sure you replace the variables. "),
              verbatimTextOutput("optClusterCode")
            ),
          )
        ),
        ##### Section 2.3 Tabset Panel #####
        conditionalPanel(
          condition = "input.uniquePatternsConfirm>0",
          fluidRow(
            box(
              width = 12,
              title = HTML("Consensus Evaluation", "<font size='3'>", 
                           as.character(actionLink(inputId = "MS_ConsensusEval", 
                                                   label = "", 
                                                   icon = icon("question-circle"))), "</font>"), 
              status = "info",
              solidHeader = TRUE, 
              tabBox(
                title = "", 
                width = 12,
                tabPanel(title = "Silouette", 
                         shiny::plotOutput("silouettePlot", height = "600px")
                ),
                tabPanel(title = "Consensus Matrix", 
                         shiny::plotOutput("consensusPlot", height = "1600px")
                ),
                tabPanel(title = "Correlation Matrix", 
                         shiny::plotOutput("correlationPlot", height = "1600px")
                )
              )
            )
          )
        )

      ),
      tabPanel(
        ## First Panel
        title = HTML("<b>Panel 2: Download Panel </b>"),
        value = "panel2",
        fluidRow(
          box( ### 5.1 Download Plot List and Button ### 
            width = 6, 
            title = HTML("Download Plots", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_DownloadPanel", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            status = "primary", solidHeader = TRUE, 
            uiOutput("plotSelector"), 
            hr(),
            ## Conditional Panels 
            conditionalPanel(
              condition = "input.plotRadioButtons=='PDF'|input.plotRadioButtons=='PNG'",
              fluidRow(
                column(
                  width = 6,
                  textInput(inputId = "heightInput",
                            label = "Height in Inch",
                            placeholder =  "Height in Inch",
                            value = "8"),
                ),
                column(
                  width = 6,
                  textInput(inputId = "widthInput",
                            label = "Width in Inch",
                            placeholder =  "Width in Inch",
                            value = "10"),
                )
              ),
              h6(HTML("Please select the heigth and width for the plot, unit: Inch. <br>
                      For both Consensus and Correlation Plot, the dimension should started around 
                      Height: 20, Width: 20, to avoid overlapping of the taxa names. 
                             ")), # The default dpi is set to 300.
              hr()
            ),
            radioGroupButtons(
              inputId = "plotRadioButtons",
              label = "Select a format for the plot",
              choices = c("PDF", "PNG"),
              justified = TRUE,
              checkIcon = list(
                yes = icon("ok", 
                           lib = "glyphicon")),
              status = "custom-status-downloadPlots"
            ), hr(),
            fluidRow(
              column(
                12,
                downloadBttn(
                  outputId = "downloadPlots",
                  label = "Download the Selected Plot", 
                  style = "gradient",
                  color = "primary"
                )
              )
            )
          )
        )
      )
    ),
    
    ##### Server #####
    server = function(input, output, session) {
      # Reactive values
      values <- reactiveValues(
        selectedMinClust = NULL, 
        selectedPatterns = NULL
      )
      plots = reactiveValues()
      
      
      ##### 2.1 Pattern UI ######
      output$uniquePatterns <- renderPlot({
        moduleCheckRemoveDup = moduleCheckRemoveDup[moduleCheckRemoveDup <= maxSizeCluster[1]]
        output$patternSelector <- renderUI({
          awesomeCheckboxGroup(
            inputId = "patternSelector",
            label = "Default selections include all unique patterns with greater or equal to ten unique modules", 
            choices = formatC(C3NAObj$misc$minModuleSize:C3NAObj$misc$maxModuleSize,flag=0,width=2), 
            selected = formatC(moduleCheckRemoveDup,flag=0,width=2),
            inline = TRUE, 
            status = "danger"
          )
        })
        
        modulePatternPlot <- 
          ggplot() +
            geom_bar(data = moduleStatV2, aes(fill = colors, x = minSize, y = nTaxa),
                     position = "fill", stat = "identity") + 
            geom_bar(data = grayScale, aes(fill = colors, x = minSize, y = nTaxa),
                     alpha = 0.5,
                     position = "fill", stat = "identity", show.legend = FALSE) +
            geom_text(data = moduleStatV2 %>% dplyr::select(minSize, totalUniqueColors), 
                      aes(x = minSize, y = 1.05, label = totalUniqueColors)) +
            geom_text(data = moduleStatV2, aes(x = 1, y = 1.05, label = paste0("#Modules:")),  
                      color = "black") +
            scale_fill_manual(breaks = c(colorTable$colors, "Removed"),
                              values = c(colorTable$randomColors, "#000000")) + theme_bw() +
            theme(legend.position = "none") + 
            labs(x = "Minimal # Taxa per Module", y ="Relative Abundance of Number of Taxa per Module",
                 caption = "Darkend bars represents duplicated patterns") + 
            scale_x_continuous(breaks= seq(C3NAObj$misc$minModuleSize,C3NAObj$misc$maxModuleSize,2), 
                               labels = seq(C3NAObj$misc$minModuleSize,C3NAObj$misc$maxModuleSize,2))
        plots[["modulePatternPlot"]] = modulePatternPlot
        modulePatternPlot
      })
      
      
      ##### 2.1.1.1 Pattern Selector ######
      output$patternSelector <- renderUI({
        awesomeCheckboxGroup(
          inputId = "patternSelector",
          label = "", 
          choices = formatC(C3NAObj$misc$minModuleSize:C3NAObj$misc$maxModuleSize,flag=0,width=2), 
          selected = formatC(moduleCheckRemoveDup,flag=0,width=2),
          inline = TRUE, 
          status = "danger"
        )
      })
      
      ##### 2.1.1.2 Optimal number of Cluster ######
      output$optNModules <- renderUI({
        sliderInput(
          inputId = "optNModules", label = "", 
          min = C3NAObj$misc$minModuleSize, max = C3NAObj$misc$maxModuleSize, 
          step = 1, value = 10
        )
      })
      
      ##### 2.1.1.3 Verbatim Text output ######
      output$optClusterCode <- renderText({ 
        if(!is.null(values[["selectedMinClust"]])){
          print(paste0("newC3NAObj = getOptMods(C3NAObj = oldC3NAObj,\n", 
                       "selectedPatterns = c(", paste0(values[["curModuleSizes"]], collapse = ","),"),\n", 
                       "nModules = ", values[["selectedMinClust"]], ")")) 
        }
      })  

      ##### 2.1.1 Update the Selected Patterns ######
      observeEvent(input$uniquePatternsConfirm, {
        values[["selectedPatterns"]] = as.numeric(input$patternSelector)
      })
      
      ##### 2.2 Silhouette Plot ######
      output$silouettePlot <- renderPlot({
        if(!is.null(values[["selectedPatterns"]])){
          withProgress(message = 'Evaluating Modular Information. Current procedure: ', value = 0, {
            ## Total Parts
            n = 3 + length(values[["selectedPatterns"]])
            counter = 1
            moduleCheckRemoveDup = as.numeric(values[["selectedPatterns"]])
            
            curminSilWitdth = 0.5
            incProgress(1/n, detail = paste("Part", counter, "Calculate the Consensus Matrix based on Selected Patterns"))
            ## Going over each of the minSizes and retrieve cluster module connection information
            curModuleSizes = moduleCheckRemoveDup[moduleCheckRemoveDup <= maxSizeCluster[1]]
            moduleData = module_df %>%
              filter(minSize %in% as.numeric(curModuleSizes)) %>%
              arrange(minSize)
            uniqueSizes = sort(unique(moduleData$minSize))
            values[["curModuleSizes"]]= curModuleSizes
            for(minN in curModuleSizes){
              incProgress(1/n, detail = paste("Part", counter, "Calculate the Consensus Matrix based on MinSize: ", minN))
              moduleData_temp = moduleData %>% filter(minSize == minN)
              consensusMatrix_temp = expand.grid(from = moduleData_temp$taxaID, to = moduleData_temp$taxaID)
              consensusMatrix_temp$Category = NA
              consensusMatrix_temp$BinarySimilarity = NA
              
              for(color in unique(moduleData_temp$colors)){
                tempD = moduleData_temp %>% filter(colors == color)
                consensusMatrix_temp = consensusMatrix_temp %>%
                  mutate(BinarySimilarity = ifelse(from %in% tempD$taxaID & to %in% tempD$taxaID, 1, BinarySimilarity))
              }
              consensusMatrix_temp$Category = paste0("MinSize_", minN)
              consensusMatrix_temp$BinarySimilarity[is.na(consensusMatrix_temp$BinarySimilarity)] = 0
              if(minN == 3){
                consensusMatrix = consensusMatrix_temp
              } else{
                consensusMatrix = rbind(consensusMatrix, consensusMatrix_temp)
              }
              counter = counter + 1
            }
            ## Expands the consensMatrix and calculate the average consensus
            consensusMatrix_wide = consensusMatrix %>%
              pivot_wider(names_from = Category, values_from = BinarySimilarity)
            consensusMatrix_wide$rowMeans = rowMeans(consensusMatrix_wide[, 3:ncol(consensusMatrix_wide)])
            consensusMatrix_Final = consensusMatrix_wide %>% dplyr::select(from, to, rowMeans)
            consensusMatrix_Final_wide = consensusMatrix_Final %>%
              pivot_wider(names_from = to, values_from = rowMeans)
            consensusMatrix_Final_wide <- consensusMatrix_Final_wide %>% column_to_rownames("from")
            values[["consensusMatrix_Final_wide"]] = consensusMatrix_Final_wide
            incProgress(1/n, detail = paste("Part", counter+1, "Generating the Silhouette Plot Data"))
            ## Calculation of the optimal cluster number based on the consensus matrix
            curHclust = cluster::agnes(x = (1-consensusMatrix_Final_wide),
                                       method = "complete", metric = "euclidean")
            values[["curHclust"]] = curHclust
            # Calculate the evals with silhouette scores
            ks = C3NAObj$misc$minModuleSize:C3NAObj$misc$maxModuleSize
            withinClusterPropZeros = rep(NA, length(ks))
            names(withinClusterPropZeros) = ks
            prop_zeros = rep(NA, length(ks))
            names(prop_zeros) = ks
            silWidths = c()
            for(k in seq_along(ks)){
              sil <- cluster::silhouette(cutree(curHclust, ks[k]), 1-consensusMatrix_Final_wide)
              silSum = summary(sil)
              avgSilWidth = silSum$avg.width
              silWidths = c(silWidths, avgSilWidth)
              
              curTree = cutree(as.hclust(curHclust), k = ks[k])
              curTreeData =  as.data.frame(curTree)
              curTreeData$Taxa = rownames(curTreeData)
              entries = c()
              nonZeros = 0
              totalWithinClusterCounts = 0
              for(x in seq(ks[k])){
                curTaxa = subset(curTreeData, curTree == x)$Taxa
                tempM = consensusMatrix_Final_wide[curTaxa, curTaxa]
                entries = c(entries, tempM[lower.tri(tempM)])
                nonZeros = nonZeros + sum(tempM!=0)
                totalWithinClusterCounts = totalWithinClusterCounts + length(as.matrix(tempM))
              }
              # calculate proportion of zeroes in M
              nTotal = length(as.matrix(consensusMatrix_Final_wide))
              withinClusterPropZeros[k] = (totalWithinClusterCounts - nonZeros)/totalWithinClusterCounts
              prop_zeros[k] = (nTotal - nonZeros)/nTotal
            }
            propZerosCut = min(as.integer(names((withinClusterPropZeros[which(withinClusterPropZeros<0.1)]))))
            
            incProgress(1/n, detail = paste("Part", counter+2, "Plotting.. "))
            # Optimal Cluster
            for(i in seq(length(silWidths)-1)){
              j = i+1
              if(silWidths[i] >= curminSilWitdth & silWidths[j] < silWidths[i] & i >= propZerosCut){
                localMax = silWidths[i]
                minClust = i+2 # i value started at 1 and k started at 3
                break
              }
            }
            values[["selectedMinClust"]] = minClust
            updateSliderInput(
              session = session, 
              inputId = "optNModules", 
              value = minClust
            )
            
            clusterInfo = data.frame(
              index = C3NAObj$misc$minModuleSize:C3NAObj$misc$maxModuleSize,
              Silouette = silWidths,
              withinClusterPropZeros = withinClusterPropZeros
            ) 
            silPlot = 
              suppressWarnings(suppressMessages(
                ggplot(clusterInfo, aes(x = index)) +
                  geom_hline(yintercept = curminSilWitdth, linetype = "dashed", color = "#add8e6", alpha = 0.7,  size = 1.5) +
                  geom_hline(yintercept = 0.1, linetype = "dotted", color = "#d94c51", alpha = 0.7,  size = 1.5) +
                  ## Silhouette - Blue
                  geom_line(aes(y = Silouette), color = "#2E40DB") +
                  geom_point(aes(y = Silouette), color = "#2736BA") +
                  ## Prop Zeros - Red
                  geom_line(aes(y = withinClusterPropZeros), color = "#DB383E") +
                  geom_point(aes(y = withinClusterPropZeros), color = "#C23237") +
                  theme_bw() +
                  labs(x = "Number of Clusters") +
                  geom_vline(xintercept = minClust, linetype = "solid", alpha = 0.3,
                             color = "#2E994C", size=1.5) +
                  geom_text(aes(x = 35, y = curminSilWitdth-0.05,
                                label = paste0("Minimal Silhouette Cutoff: ", curminSilWitdth)),
                            color = "#add8e6") +
                  geom_text(aes(x = 35, y = 0.07,
                                label = paste0("10% Proportion of Zero Threshold")),
                            color = "#d94c51") +
                  
                  labs(title = paste0("Initial Estimate for the Optimal Number of Modules: ", minClust),
                       caption = paste0("Consensus Matrix Generated based on Selected Module Patterns")) +
                  ylim(0, 1) +
                  scale_y_continuous(
                    name = "Average Silhouette Width",
                    sec.axis = sec_axis(~., name = "Within Consensus k's Module Proprotion of Zeros",
                                        labels = function(b) { paste0(round(b * 100, 0), "%")})
                  ) +
                  theme(axis.title.y.left = element_text(colour = "#2736BA",
                                                         face = "bold"),
                        axis.title.y.right = element_text(colour = "#C23237",
                                                          face = "bold"),
                        axis.text.y.left = element_text(color = "#2736BA"),
                        axis.text.y.right = element_text(color = "#C23237"),
                        plot.title = element_text(color = "#2E994C", face = "italic")) +
                  scale_x_continuous(breaks= seq(5,40,5),
                                     labels = seq(5,40,5))
              ))
            plots[["silPlot"]] = silPlot
            silPlot
          })  
        }
      })
      

      ##### 2.1.1.4 MultiListener ######
      consensusListeners <- reactive({
        list(input$uniquePatternsConfirm,
             input$updateClusters)
      })
      
      observeEvent(input$optNModules, {
        values[["selectedMinClust"]] = input$optNModules
      })
      
      observeEvent(consensusListeners(), {
        ##### 2.1.2 Consensus Matrix ######
        output$consensusPlot <- renderPlot({
          withProgress(message = 'Generating Consensus Plot, please wait...', value = 0, {
            n=3
            incProgress(1/n, detail = paste("Part", 1, "calculate Consensus Data"))
            if(!is.null(values[["selectedMinClust"]])){
              minClust = values[["selectedMinClust"]]
              curHclust = values[["curHclust"]]
              consensusMatrix_Final_wide = values[["consensusMatrix_Final_wide"]]

              # Obtain the clustering information for the taxa
              curTree = cutree(as.hclust(curHclust), k = minClust)
              ## Generate Pheatmap
              my_taxa_colData_NbClust <- as.data.frame(curTree)
              colnames(my_taxa_colData_NbClust)[1] = "Cluster"
              my_taxa_colData_NbClust$Cluster = as.character(paste0("Cluster ", 
                                                                    formatC(my_taxa_colData_NbClust$Cluster,flag=0,width=2)))
              my_taxa_colData_NbClust$TaxaName = rownames(my_taxa_colData_NbClust)
              my_taxa_colData_NbClust = my_taxa_colData_NbClust %>% as_tibble() %>%
                mutate(taxaLevelAbbrev = sapply(strsplit(TaxaName, "_"), "[", 1)) %>%
                mutate(taxaLevel = case_when(
                  taxaLevelAbbrev =="p" ~ "Phylum",
                  taxaLevelAbbrev =="c" ~ "Class",
                  taxaLevelAbbrev =="o" ~ "Order",
                  taxaLevelAbbrev =="f" ~ "Family",
                  taxaLevelAbbrev =="g" ~ "Genus", 
                  taxaLevelAbbrev =="s" ~ "Species",
                )) %>% dplyr::select(-taxaLevelAbbrev) %>%
                column_to_rownames("TaxaName")
              
              my_taxa_colData_NbClust_Left = my_taxa_colData_NbClust[, c("Cluster", "taxaLevel")]
              my_taxa_colData_NbClust_Left$Phylum = as.character(ifelse(my_taxa_colData_NbClust_Left$taxaLevel=="Phylum", TRUE, FALSE))
              my_taxa_colData_NbClust_Left$Class = as.character(ifelse(my_taxa_colData_NbClust_Left$taxaLevel=="Class", TRUE, FALSE))
              my_taxa_colData_NbClust_Left$Order = as.character(ifelse(my_taxa_colData_NbClust_Left$taxaLevel=="Order", TRUE, FALSE))
              my_taxa_colData_NbClust_Left$Family = as.character(ifelse(my_taxa_colData_NbClust_Left$taxaLevel=="Family", TRUE, FALSE))
              my_taxa_colData_NbClust_Left$Genus = as.character(ifelse(my_taxa_colData_NbClust_Left$taxaLevel=="Genus", TRUE, FALSE))
              my_taxa_colData_NbClust_Left$Species = as.character(ifelse(my_taxa_colData_NbClust_Left$taxaLevel=="Species", TRUE, FALSE))
              my_taxa_colData_NbClust_Right = my_taxa_colData_NbClust_Left
              
              colors50 = c(
                "#C1F242", "#68C5E5", "#A3E86B", "#E2DE48", "#CFE5AE",
                "#EBF1D8", "#55B99F", "#C8EEEC", "#B7A7DF", "#E1CBA3",
                "#A9CBB4", "#E597DE", "#929776", "#C3E67B", "#B08BEA",
                "#82F23A", "#E9B6D6", "#A38B97", "#D12FEE", "#54ECB1",
                "#DCD263", "#DB7341", "#E37EA0", "#B053E2", "#EAF425",
                "#9CEEC8", "#9453A6", "#DC4B9C", "#E0506A", "#ACA550",
                "#69A5DE", "#63EB73", "#7CA3A9", "#E7CCCA", "#E33ED1",
                "#5C74EC", "#C6CEE6", "#A4E9A1", "#E0948A", "#7E3C62",
                "#E5AD35", "#66F0E3", "#6CB061", "#8DBB28", "#DD72E1",
                "#613CD6", "#6382D5", "#7ADCE0", "#E5EA96", "#E2AE74"
              ) 
              ## Max 15 Cluster Colors
              curClusterColors = colors50[1:minClust]
              taxaLevel_colors = c("#FFD919", "#0B701E", "#FF7300", "#120C94", "#FF748C", "#009BB3")
              names(taxaLevel_colors) = c("Phylum", "Class", "Order", "Family", "Genus", "Species")
              
              taxaLevel_p = c("#FFEC9D","#000000") # Yellow
              names(taxaLevel_p) = c("TRUE", "FALSE")  
              
              taxaLevel_c = c("#93C797", "#000000") # Green
              names(taxaLevel_c) = c("TRUE", "FALSE")
              
              taxaLevel_o = c("#FFC19F", "#000000") # Orange 
              names(taxaLevel_o) = c("TRUE", "FALSE")
              
              taxaLevel_f = c("#8194BA", "#000000") # Navy
              names(taxaLevel_f) = c("TRUE", "FALSE")
              
              taxaLevel_g = c("#FFC1DA", "#000000") # Pink
              names(taxaLevel_g) = c("TRUE", "FALSE")
              
              taxaLevel_s = c("#89F3F5", "#000000") # Cerulean
              names(taxaLevel_s) = c("TRUE", "FALSE")
              
              clusterColors = curClusterColors
              names(clusterColors) = sort(unique(my_taxa_colData_NbClust_Left$Cluster))
              colors = list(
                # Altered = altered_colors, 
                taxaLevel = taxaLevel_colors, 
                Phylum = taxaLevel_p, 
                Class = taxaLevel_c, 
                Order = taxaLevel_o, 
                Family = taxaLevel_f, 
                Genus = taxaLevel_g,
                Species = taxaLevel_s,
                Cluster = clusterColors
              )
              
              groups <- as.data.frame(curTree)
              order = rownames(consensusMatrix_Final_wide[curHclust[["order"]],])
              groupFreqTable = as.data.frame(table(groups[order,]))
              curClusterTable = data.frame(taxaName = order, clusterID = groups[order,])
              clusterFreq = groupFreqTable[unique(groups[order,]), ]$Freq
              
              ## Reorder 
              my_taxa_colData_NbClust_Right = my_taxa_colData_NbClust_Left = my_taxa_colData_NbClust_Left[order,]
              
              rowIndices = c()
              for(i in seq_along(clusterFreq)){
                rowIndices = c(rowIndices, sum(clusterFreq[1:i]))
              }
              rowIndices = rowIndices[1:(length(rowIndices)-1)]
              colorHex = colorRampPalette(rev(brewer.pal(n=11, name="RdBu")))(51)
              colorScale = seq(-1, 1, length.out = 51)
              reorderdConsensus = as.matrix(consensusMatrix_Final_wide)[order, order]
              diag(reorderdConsensus) = NA
              incProgress(1/n, detail = paste("Part", 2, "Generate Consensus Plot"))
              consensusPlot = pheatmap::pheatmap(mat = reorderdConsensus, 
                                                 main = paste0("# Optimal Clusters: ", minClust, " | ",
                                                               "Phenotype: ", C3NAObj$misc$phenotype, "; "),
                                                 annotation_row = my_taxa_colData_NbClust_Left, 
                                                 annotation_colors = colors, 
                                                 annotation_col = my_taxa_colData_NbClust_Right, 
                                                 fontsize_col = 4, fontsize_row = 4,
                                                 gaps_row = rowIndices,gaps_col = rowIndices,
                                                 cluster_rows = FALSE, cluster_cols = FALSE)
              plots[["consensusPlot"]] = consensusPlot
              consensusPlot
              incProgress(1/n, detail = paste("Part", 3, "Saving..."))
            }
          })
        })
        
        ##### 2.1.3 Correlation Matrix ######
        output$correlationPlot <- renderPlot({
          withProgress(message = 'Generating Correlation Plot, please wait...', value = 0, {
            n=3
            incProgress(1/n, detail = paste("Part", 1, "calculate Correlation Data"))
            if(!is.null(values[["selectedMinClust"]])){
              minClust = values[["selectedMinClust"]]
              curHclust = values[["curHclust"]]
              consensusMatrix_Final_wide = values[["consensusMatrix_Final_wide"]]
              ## Plot the same plot with correlation
              SparccP2 = C3NAObj$sparCCTable %>% 
                mutate(Var3 = Var1) %>% 
                select(-Var1) %>%
                rename(Var1 = Var2) %>%
                rename(Var2 = Var3) %>%
                filter(Var1 != Var2)
              
              reordered_sparccCor = C3NAObj$sparCCTable %>%
                rbind(SparccP2) %>%
                select(-p, -fdr) %>%
                pivot_wider(names_from = Var2, values_from = cor) %>%
                column_to_rownames("Var1")
              # Obtain the clustering information for the taxa
              curTree = cutree(as.hclust(curHclust), k = minClust)
              
              ## Generate Pheatmap
              my_taxa_colData_NbClust <- as.data.frame(curTree)
              colnames(my_taxa_colData_NbClust)[1] = "Cluster"
              my_taxa_colData_NbClust$Cluster = as.character(paste0("Cluster ", 
                                                                    formatC(my_taxa_colData_NbClust$Cluster,flag=0,width=2)))
              my_taxa_colData_NbClust$TaxaName = rownames(my_taxa_colData_NbClust)
              my_taxa_colData_NbClust = my_taxa_colData_NbClust %>% as_tibble() %>%
                mutate(taxaLevelAbbrev = sapply(strsplit(TaxaName, "_"), "[", 1)) %>%
                mutate(taxaLevel = case_when(
                  taxaLevelAbbrev =="p" ~ "Phylum",
                  taxaLevelAbbrev =="c" ~ "Class",
                  taxaLevelAbbrev =="o" ~ "Order",
                  taxaLevelAbbrev =="f" ~ "Family",
                  taxaLevelAbbrev =="g" ~ "Genus", 
                  taxaLevelAbbrev =="s" ~ "Species",
                )) %>% dplyr::select(-taxaLevelAbbrev) %>%
                column_to_rownames("TaxaName")
              
              my_taxa_colData_NbClust_Left = my_taxa_colData_NbClust[, c("Cluster", "taxaLevel")]
              my_taxa_colData_NbClust_Left$Phylum = as.character(ifelse(my_taxa_colData_NbClust_Left$taxaLevel=="Phylum", TRUE, FALSE))
              my_taxa_colData_NbClust_Left$Class = as.character(ifelse(my_taxa_colData_NbClust_Left$taxaLevel=="Class", TRUE, FALSE))
              my_taxa_colData_NbClust_Left$Order = as.character(ifelse(my_taxa_colData_NbClust_Left$taxaLevel=="Order", TRUE, FALSE))
              my_taxa_colData_NbClust_Left$Family = as.character(ifelse(my_taxa_colData_NbClust_Left$taxaLevel=="Family", TRUE, FALSE))
              my_taxa_colData_NbClust_Left$Genus = as.character(ifelse(my_taxa_colData_NbClust_Left$taxaLevel=="Genus", TRUE, FALSE))
              my_taxa_colData_NbClust_Left$Species = as.character(ifelse(my_taxa_colData_NbClust_Left$taxaLevel=="Species", TRUE, FALSE))
              my_taxa_colData_NbClust_Right = my_taxa_colData_NbClust_Left
              
              colors50 = c(
                "#C1F242", "#68C5E5", "#A3E86B", "#E2DE48", "#CFE5AE",
                "#EBF1D8", "#55B99F", "#C8EEEC", "#B7A7DF", "#E1CBA3",
                "#A9CBB4", "#E597DE", "#929776", "#C3E67B", "#B08BEA",
                "#82F23A", "#E9B6D6", "#A38B97", "#D12FEE", "#54ECB1",
                "#DCD263", "#DB7341", "#E37EA0", "#B053E2", "#EAF425",
                "#9CEEC8", "#9453A6", "#DC4B9C", "#E0506A", "#ACA550",
                "#69A5DE", "#63EB73", "#7CA3A9", "#E7CCCA", "#E33ED1",
                "#5C74EC", "#C6CEE6", "#A4E9A1", "#E0948A", "#7E3C62",
                "#E5AD35", "#66F0E3", "#6CB061", "#8DBB28", "#DD72E1",
                "#613CD6", "#6382D5", "#7ADCE0", "#E5EA96", "#E2AE74"
              ) 
              ## Max 15 Cluster Colors
              curClusterColors = colors50[1:minClust]
              taxaLevel_colors = c("#FFD919", "#0B701E", "#FF7300", "#120C94", "#FF748C", "#009BB3")
              names(taxaLevel_colors) = c("Phylum", "Class", "Order", "Family", "Genus", "Species")
              
              taxaLevel_p = c("#FFEC9D","#000000") # Yellow
              names(taxaLevel_p) = c("TRUE", "FALSE")  
              
              taxaLevel_c = c("#93C797", "#000000") # Green
              names(taxaLevel_c) = c("TRUE", "FALSE")
              
              taxaLevel_o = c("#FFC19F", "#000000") # Orange 
              names(taxaLevel_o) = c("TRUE", "FALSE")
              
              taxaLevel_f = c("#8194BA", "#000000") # Navy
              names(taxaLevel_f) = c("TRUE", "FALSE")
              
              taxaLevel_g = c("#FFC1DA", "#000000") # Pink
              names(taxaLevel_g) = c("TRUE", "FALSE")
              
              taxaLevel_s = c("#89F3F5", "#000000") # Cerulean
              names(taxaLevel_s) = c("TRUE", "FALSE")
              
              clusterColors = curClusterColors
              names(clusterColors) = sort(unique(my_taxa_colData_NbClust_Left$Cluster))
              colors = list(
                # Altered = altered_colors, 
                taxaLevel = taxaLevel_colors, 
                Phylum = taxaLevel_p, 
                Class = taxaLevel_c, 
                Order = taxaLevel_o, 
                Family = taxaLevel_f, 
                Genus = taxaLevel_g,
                Species = taxaLevel_s,
                Cluster = clusterColors
              )
              
              groups <- as.data.frame(curTree)
              order = rownames(consensusMatrix_Final_wide[curHclust[["order"]],])
              groupFreqTable = as.data.frame(table(groups[order,]))
              curClusterTable = data.frame(taxaName = order, clusterID = groups[order,])
              clusterFreq = groupFreqTable[unique(groups[order,]), ]$Freq
              
              ## Reorder 
              my_taxa_colData_NbClust_Right = my_taxa_colData_NbClust_Left = my_taxa_colData_NbClust_Left[order,]
              
              rowIndices = c()
              for(i in seq_along(clusterFreq)){
                rowIndices = c(rowIndices, sum(clusterFreq[1:i]))
              }
              rowIndices = rowIndices[1:(length(rowIndices)-1)]
              colorHex = colorRampPalette(rev(brewer.pal(n=11, name="RdBu")))(51)
              colorScale = seq(-1, 1, length.out = 51)
              reorderdConsensus = as.matrix(reordered_sparccCor)[order, order]
              diag(reorderdConsensus) = NA
              incProgress(1/n, detail = paste("Part", 2, "Generate Correlation Plot"))
              correlationPlot = pheatmap::pheatmap(mat = reorderdConsensus, 
                                                   main = paste0("# Optimal Clusters: ", minClust, " | ",
                                                                 "Phenotype: ", C3NAObj$misc$phenotype, "; "),
                                                   annotation_row = my_taxa_colData_NbClust_Left, 
                                                   annotation_colors = colors, 
                                                   color = colorHex, breaks = colorScale,
                                                   annotation_col = my_taxa_colData_NbClust_Right, 
                                                   fontsize_col = 4, fontsize_row = 4,
                                                   gaps_row = rowIndices,gaps_col = rowIndices,
                                                   cluster_rows = FALSE, cluster_cols = FALSE)
              plots[["correlationPlot"]] = correlationPlot
              correlationPlot
              incProgress(1/n, detail = paste("Part", 3, "Saving..."))
            }
          })
        })
      })
      
      
      ##### Download Panel ###### 
      output$plotSelector <- renderUI({
        if(input$plotRadioButtons %in% c("PNG", "PDF")){
          ## HTML Plots
          avaliablePlots <- sort(names(plots))
          listOfImportantData <- data.frame(
            original = c("modulePatternPlot", "silPlot", 
                         "consensusPlot", "correlationPlot"),
            full = c("Module Pattern Plot", "Silhouette Evaluation Plot",
                     "Consensus Plot", "Correlation Plot"))
          checker <- avaliablePlots %in% listOfImportantData$original
          tempData <- avaliablePlots[checker]
          listData <- listOfImportantData %>%
            filter(original %in% tempData)
          
          ## Obtain the list of plots available
          if(nrow(listData) < 1){
            h4("No plot was generated in Panel 1. Please check your data. ")
          } else {
            radioGroupButtons(
              inputId = "plotSelector",
              choiceNames = listData$full, 
              choiceValues = listData$original,
              individual = TRUE,
              checkIcon = list(
                yes = tags$i(class = "fa fa-check-square", 
                             style = "color: white"),
                no = tags$i(class = "fa fa-square-o", 
                            style = "color: black")),
              status = "custom-status-downloadPlots"
            )
          }
        }
      })
      
      ## Observe the download button and handle it
      selectedPlotInput <- function(){
        req(input$plotSelector)
        plots[[input$plotSelector]]
      }
      
      # create filename
      selectedPlotName <- reactive({
        name <- input$plotSelector
        if(input$plotRadioButtons == "PDF")   filename <- paste0(name, "-", Sys.Date(), ".pdf",  sep="")
        if(input$plotRadioButtons == "PNG")   filename <- paste0(name, "-", Sys.Date(), ".png",  sep="")
        return(filename)
      })
      
      output$downloadPlots <- downloadHandler(
        filename = selectedPlotName,
        content = function(file) {
          name <- input$plotSelector
          
          if(input$plotRadioButtons %in% c("PDF")) {
            ggsave(file, plot = selectedPlotInput(), 
                   height = as.numeric(input$heightInput), 
                   width = as.numeric(input$widthInput), units = "in")
          } else if(input$plotRadioButtons == "PNG") {
            ggsave(file, plot = selectedPlotInput(), dpi = 300, 
                   height = as.numeric(input$heightInput), 
                   width = as.numeric(input$widthInput), units = "in")
          }
        }
      )
      
      ##### 3.0 All Showmodels ######
      observeEvent(input$MS_PatternVis, {
        showModal(
          modalDialog(
            title = HTML('<b>Visualization of Module Patterns across Different Minimal Module Sizes</b>'),
            HTML("
The 'Pattern Visualization' section hosts the patterns in turns of the unique module due to the different minimal numbers of taxa per module. <hr>
The <b>x-axis</b> represents the different range of a minimal number of taxa per module; by default, it investigated between 3 and 40, and this range should be sufficient for observing all the necessary patterns. 
<br>
The <b>y-axis</b> represents The relative abundance of taxa per module.The colors are assigned using 'randomcolorR' package with a seed, which can be adjusted by setting the 'colorSeed' arguments when calling this Shiny application. 
<br>
The number of unique modules is displayed on top of the bar plot. 
<hr>
The plot can be saved from 'Panel 2' on the top bar. 
              "),
easyClose = TRUE
          )
        )
      })
      
      observeEvent(input$MS_UniquePatternSelector, {
        showModal(
          modalDialog(
            title = HTML('<b>Selection of Unique Patterns that will be included in Consensus Clustering</b>'),
            HTML("
The unique pattern selector enables you to manually adjust which patterns will be included to generate the consensus matrix. 
The optimal last pattern is generally around when the number of unique patterns drops to 10, with more <p style='background-color:gray;display:inline;color:white;'>prevalent duplicated patterns (darkened bar)</p> compared to patterns with more unique modules. 
Please check the supplement results from our publication for a more detailed investigation of the impact of different selected patterns.               "),
easyClose = TRUE
          )
        )
      })
      
      observeEvent(input$MS_OptimalModuleSelector, {
        showModal(
          modalDialog(
            title = HTML('<b>Manually Confirm the Number of Modules based on Consensus Clustering Results</b>'),
            HTML("
The Optimal Module Selector should be determined by the Silhouette, Consensus, and Correlation Plots below, with the Silhouette plot being the main factor. <hr>
The <b><font color='#2736BA'>Silhouette curve</font></b>  (based on the consensus matrix, in blue) should follow the pattern of quick incrementation with the increased number of clusters followed by a plateau region where the incrementation rate is much slower. 
The optimal number of clusters should be around the turning point between these two clusters with a lower than 10% proportion of intra-modular zeros, which is shown in the <b><font color='#C23237'>intra-modular proportion of zero curve</font></b>. 
<hr>
Please check the supplement results from our publication for a more detailed investigation of this topic. 

              "),
easyClose = TRUE
          )
        )
      })
      
      observeEvent(input$MS_OptimalModuleCodeGenerator, {
        showModal(
          modalDialog(
            title = HTML('<b>Manually Confirm the Number of Modules based on Consensus Clustering Results</b>'),
            HTML(
              "The code generator displays the code for the user to run after confirming the optimal pattern selection and the optimal number of clusters of taxa based on the consensus matrix. The user should select the display text and copy&paste it to R to run after closing the Shiny application. Please replace the 'oldC3NAObj' and 'newC3NAObj' with the correct and preferred name. <hr><b>Note: </b> The code is also displayed on the console. 
"
            ),
easyClose = TRUE
          )
        )
      })
      
      observeEvent(input$MS_ConsensusEval, {
        showModal(
          modalDialog(
            title = HTML('<b>Optimal Number of Module Selection Based on the Consensus Results</b>'),
            HTML(
              "The consensus eval section hosts the three module evaluation tools, the Silhouette Plot, Consensus Plot, and Correlation Plot. These should be used to determine the optimal number of clusters based on the consensus matrix generated using the selected patterns. <hr>
The <b><font color='#2736BA'>Silhouette curve</font></b>  (based on the consensus matrix, in blue) should follow the pattern of quick incrementation with the increased number of clusters followed by a plateau region where the incrementation rate is much slower. 
The optimal number of clusters should be around the turning point between these two clusters with a lower than 10% proportion of intra-modular zeros, which is shown in the <b><font color='#C23237'>intra-modular proportion of zero curve</font></b>. <br>
The <b>Consensus Plot</b> includes the consensus heatmap with the taxa name, taxonomic levels, and clusters shown. <br>
The <b>Correlation Plot</b> includes the correlation heatmap with the taxa name, taxonomic levels, and clusters shown. <br>
<hr>
To update the number of clusters for the consensus and correlation plot, please use the 'Confirm Selected Patterns' above. The legend for the clusters might not be displayed fully within the shiny display due to limited dimensions; please visit the download panel to select a wider width for the correct display of the cluster legend. The plot can be saved from 'Panel 2' on the top bar. "

            ),
easyClose = TRUE
          )
        )
      })
      
      observeEvent(input$MS_DownloadPanel, {
        showModal(
          modalDialog(
            title = HTML('<b>Download Displayed Figures in Panel 1</b>'),
            HTML(
"The download panel enables you to download the currently displayed plots from Panel 1 in PDF or PNG format. For the PNG format, the DPI is set to 300. The file should be saved to your default download folder with the plot type name followed by the date and time to avoid overwriting any plot. "            
            ),
easyClose = TRUE
          )
        )
      })
      
      # stop App on closing the browser
      session$onSessionEnded(function() {
        stopApp()
      })
      
    },
    # launch App in a browser
    options = list(launch.browser = TRUE)
  )
}


#' Shiny Application for C3NAobj Comparison 
#' 
#' @description This function calls the interactive two phenotypes after running the 
#' comparePhenotypes() step. 
#' 
#' @param C3NAObj (Required) The C3NAObj with Compared Phenotypes
#' 
#' @rawNamespace import(plotly, except = last_plot)
#' @import shinyWidgets
#' @import shinydashboard
#' @import visNetwork 
#' @import reactable
#' @importFrom DT renderDataTable dataTableOutput datatable
#' @importFrom base64enc dataURI
#' @importFrom htmlwidgets saveWidget
#' @export
#' 
compareTwoPhenoShiny = function(C3NAObj){
  ## Quick Check
  if(is.null(C3NAObj$misc$corCut)){
    stop("Please run the comparePhenotypes() before running the interactive Shiny application. ")
  }
  ## Extract the needed data for Shiny
  sparccP_Filtered_rbind = C3NAObj$sparccP_Filtered_rbind
  sparccP_Filtered_Combined = C3NAObj$sparccP_Filtered_Combined
  modulePlotData_Wide = C3NAObj$modulePreservation$modulePlotData_Wide
  nodesAll = C3NAObj$nodes$nodesAll
  ## Filter nodes All 
  uniqueTaxa = unique(unlist(sparccP_Filtered_Combined[, c("target", "source")]))
  nodesAll = subset(nodesAll, TaxaName %in% uniqueTaxa)
  nodesAll_Pheno = merge(nodesAll, C3NAObj$nodes$refTaxaTable, 
                   by.x = "TaxaName", by.y = "taxaName", all.x = TRUE)
  nodesAll_Pheno = nodesAll_Pheno %>%
    mutate(taxaLevelAbbrev = sapply(strsplit(TaxaName, "_"), "[", 1)) %>%
    mutate(`Taxonomic Levels` = case_when(
      taxaLevelAbbrev =="p" ~ "Phylum",
      taxaLevelAbbrev =="c" ~ "Class",
      taxaLevelAbbrev =="o" ~ "Order",
      taxaLevelAbbrev =="f" ~ "Family",
      taxaLevelAbbrev =="g" ~ "Genus", 
      taxaLevelAbbrev =="s" ~ "Species"
    ),
    `Number of Unique Taxa` = 1)
  nodesAll_PhenoOri = nodesAll_Pheno
  statsTable = C3NAObj$statsTable %>%
    filter(`Full Taxa Name` %in% nodesAll$TaxaName)
  nodesAbbre = C3NAObj$nodes$nodesAbbre
  samePhyloTable = C3NAObj$nodes$samePhyloTable
  C3NAResults = C3NAObj$C3NA_Wilcoxon
  data1Name = C3NAObj$miscDisease$phenotype
  data2Name = C3NAObj$miscControl$phenotype
  curDisease_OriCountTable = C3NAObj$curDisease_OriCountTable
  curControl_OriCountTable = C3NAObj$curControl_OriCountTable
  corCut = C3NAObj$misc$corCut
  fdr = C3NAObj$misc$fdr
  removedTaxaTable = C3NAObj$removedTaxaTable
  consensusProp_Disease = C3NAObj$consensusDisease$consensusProp
  consensusProp_Disease = consensusProp_Disease %>%
    select(ConsensusProp, taxaComboName) %>%
    rename(DiseaseConsensusProp = ConsensusProp) %>%
    mutate(DiseaseConsensusProp = round(DiseaseConsensusProp, digits = 4))
  consensusProp_Control = C3NAObj$consensusControl$consensusProp 
  consensusProp_Control = consensusProp_Control %>%
    select(ConsensusProp, taxaComboName) %>%
    rename(ControlConsensusProp = ConsensusProp) %>%
    mutate(ControlConsensusProp = round(ControlConsensusProp, digits = 4))
  phenotypes = c(data1Name, data2Name)
  ## DA Methods if present
  daData = C3NAObj$DA
  daMethods = names(sapply(daData, class))[as.vector(as.vector(sapply(daData, class))) == "logical"]
  daMethods = c(daMethods, "None")
  
  shinyApp(
    ui = navbarPage(
      title = "C3NA",
      tags$head(
        tags$style(
          HTML(
            ".checkbox-inline { 
                    margin-left: 0px;
                    margin-right: 10px;
              }
             .checkbox-inline+.checkbox-inline {
                        margin-left: 0px;
                        margin-right: 10px;
             }
            .multi-wrapper {height: 415px !important;}
            .multi-wrapper .non-selected-wrapper, .multi-wrapper .selected-wrapper {height: 370px !important;}
            img {float: left;}
            .myHeader {
              font-weight: 900; font-size: 20px; margin: 0 -600rem; padding: 0.15rem 600rem;
              background: rgba(250, 205, 15, 0.2); /*Original: 666*/
              border-bottom: 5rem; -webkit-transition: all .5s ease-in-out;
              -moz-transition: all .5s ease-in-out; -o-transition: all .5s ease-in-out;
              transition: all .5s ease-in-out; text-decoration: underline solid transparent;
              box-shadow: 0 0 11px;
            }
            .btn-custom-status-downloadPlots:hover, .btn-custom-status-downloadPlots:active, 
            .btn-custom-status-downloadPlots:focus, .btn-custom-status-downloadPlots:visited, 
            .btn-custom-status-downloadPlots.active, .open .dropdown-toggle.btn-custom-status-downloadPlots { 
              color: #ffffff;
              background-color: #318dc3;
              border-color: #07486f;
              box-shadow: inset 4px 4px 4px 0 #104667,
                          inset -4px -4px 4px 0 #5ebdf7;
            }
            .btn-custom-status-downloadPlots { 
              color: black; 
              background-color: #88bcdc ; 
              border-color: #07486f;
              box-shadow: 4px 4px 4px 0 rgba(0, 0, 0, 0.2),
                          -4px -4px 4px 0 rgba(255, 255, 255, 0.5),
                          inset 3px 3px 3px 0 #bdd5e4,
                          inset -3px -3px 3px 0 #0c3a56;
            } 
            "
          )
        ) 
      ),
      tabPanel(
        ## First Panel
        title = HTML("<b>Panel 1: Compare Two Phenotypes</b>"),
        value = "panel1",
        useShinyjs(),
        useShinydashboard(),
        setShadow(class = "box"),
        
        tags$div(class = "Banner", 
                 tags$div(class = "myHeader", id = "infoPanel1Sec1",
                          (HTML("Section 1: Dataset Information")
                          )
                 )
        ),
        br(),
        ##### Section 1 General Taxa #####
        fluidRow(
          box(
            width = 3,
            title = HTML("Comparison Phenotype", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_Pheno1", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            
            status = "warning",
            solidHeader = TRUE, # height = "33em", 
            plotlyOutput("pheno1Sunburst")
          ),
          box(
            width = 3,
            title = HTML("Reference Phenotype", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_Pheno2", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            
            status = "warning",
            solidHeader = TRUE, # height = "33em", 
            plotlyOutput("pheno2Sunburst")
          ),
          box(
            width = 6,
            title = HTML("Phenotype-based Taxa Comparison", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_PhenoComparison", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            
            status = "warning",
            solidHeader = TRUE, # height = "33em", 
            reactable::reactableOutput("taxaRelatedStatTable")
          )
        ),
        
        tags$div(class = "Banner", 
                 tags$div(class = "myHeader", id = "infoPanel1Sec1",
                          (HTML("Section 2: Preservation Statistics")
                          )
                 )
        ),
        br(),
        ##### Section 2 Taxa Statistics #####
        fluidRow(
          box(
            width = 4,
            title = HTML("Taxa Comparison among Modules from Two Phenotypes", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_ModModComparison", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 

            status = "info",
            solidHeader = TRUE, height = "33em", 
            plotlyOutput("moduleModuleComparison")
          ),
          box(
            width = 4,
            title = HTML("Module Preservation Statistics", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_ModPreservation", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            
            status = "info",
            solidHeader = TRUE,  height = "33em", 
            plotlyOutput("modulePreservation")
          ),
          box(
            width = 4,
            title = HTML("Module Membership", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_ModMembership", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            status = "info",
            solidHeader = TRUE, height = "33em", 
            DT::dataTableOutput("clickedModuleInfo")
          )
        ),
        #####  Section 3 Taxa Specific investigation #####
        tags$div(class = "Banner", 
                 tags$div(class = "myHeader", id = "infoPanel1Sec1",
                          (HTML("Section 3: Taxa Investigation")
                          )
                 )
        ),
        br(),
        fluidRow(
          box(
            width = 4,
            title = HTML("Taxa Selector", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_TaxaSelector", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            status = "danger",
            solidHeader = TRUE,  height = "36em", 
            uiOutput("taxaSelector")
          ), 
          box(
            width = 5,
            title = HTML("Phenotype-Specific Taxa Membership", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_ModBarPlot", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            status = "danger",
            solidHeader = TRUE, height = "36em", 
            ##### . 4.1 VisNetwork Drop Down #####
            dropdown(
              radioGroupButtons(
                inputId = "barPlotPhenoSelector",
                label = "Phenotype Selector:", 
                choices = phenotypes,
                selected = phenotypes[1],
                status = "custom-status"
              ),
              uiOutput("barPlotTaxaSelector"),
              circle = TRUE, status = "danger", 
              icon = icon("filter"), width = "600px",
              animate = animateOptions(
                enter = shinyWidgets::animations$bouncing_entrances$bounceInLeft,
                exit = shinyWidgets::animations$bouncing_exits$bounceOutRight
              ),
              tooltip = tooltipOptions(title = "Click to Switch Phenotypes and Selected Taxa")
            ),
            plotlyOutput("barPlot")
          ),
          box(
            width = 3,
            title = HTML("Selected Taxa Modular Information", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_TaxaModTable", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            status = "danger",
            solidHeader = TRUE, height = "36em", 
            
            dropdown(
              uiOutput("taxaModularTaxaSelector"),
              circle = TRUE, status = "danger", 
              icon = icon("filter"), width = "300px",
              animate = animateOptions(
                enter = shinyWidgets::animations$bouncing_entrances$bounceInRight,
                exit = shinyWidgets::animations$bouncing_exits$bounceOutRight
              ),
              tooltip = tooltipOptions(title = "Click to Switch Taxon")
            ),
            hr(),
            DT::dataTableOutput("intramodularCorTable")
          )
        ),
        ##### Section 3 Network Plot #####
        conditionalPanel(
          condition = "input.refreshPanel1Section3==0",
          fluidRow(
            box(
              width = 4,
              title = HTML("Proceed to Network", "<font size='3'>", 
                           as.character(actionLink(inputId = "MS_ClickToProceeed", 
                                                   label = "", 
                                                   icon = icon("question-circle"))), "</font>"), 
              status = "success",
              solidHeader = TRUE,
              actionBttn(
                inputId = "refreshPanel1Section3",
                label = "Plot Network based on Selection",
                style = "material-flat",
                color = "primary"
              )
            )
          )
        ),
        conditionalPanel(
          condition = "input.refreshPanel1Section3>0",
          fluidRow(
            box(
              width = 8,
              title = HTML("Network Plot", "<font size='3'>", 
                           as.character(actionLink(inputId = "MS_Network", 
                                                   label = "", 
                                                   icon = icon("question-circle"))), "</font>"), 
              status = "success",
              height = "63em",
              solidHeader = TRUE,
              ##### . 4.1 VisNetwork Drop Down #####
              dropdown(
                prettyRadioButtons(
                  inputId = "nodesMode",
                  label = "Node-Node Connections Mode:",
                  selected = "selectedTaxa",
                  choiceNames =  c("Only the selected taxa",
                                   "All taxa directly related to selected taxa",
                                   "All taxa in related modules"),
                  choiceValues = c("selectedTaxa",
                                   "relatedTaxa",
                                   "allRelatedTaxa"),
                  status = "primary",
                  fill = TRUE
                ),
                radioGroupButtons(
                  inputId = "samePhylo",
                  label = "Remove Connection from Same Phylogenenetic Branch?",
                  choices  = c("Remove", "Keep"),
                  selected = c("Keep"),
                  checkIcon = list(
                    yes = tags$i(class = "fa fa-check-square",
                                 style = "color: steelblue"),
                    no = tags$i(class = "fa fa-square-o",
                                style = "color: steelblue"))
                ),
                checkboxGroupButtons(
                  inputId = "corMode",
                  label = "Edges Mode:",
                  choiceNames = c("Disease-Only",
                                  "Control-Only",
                                  "Shared"),
                  choiceValues = c("diseaseOnly",
                                   "controlOnly",
                                   "both"),
                  selected = c("diseaseOnly",
                               "controlOnly",
                               "both"),
                  checkIcon = list(
                    yes = tags$i(class = "fa fa-check-square",
                                 style = "color: steelblue"),
                    no = tags$i(class = "fa fa-square-o",
                                style = "color: steelblue"))
                ),
                checkboxGroupButtons(
                  inputId = "taxaLvls",
                  label = "Taxonomic Levels:",
                  choiceNames = c("Phylum", "Class", "Order", "Family", "Genus", "Species"),
                  choiceValues = c("p", "c", "o", "f", "g", "s"),
                  selected = c("p", "c", "o", "f", "g", "s"),
                  checkIcon = list(
                    yes = tags$i(class = "fas fa-virus",
                                 style = "color: steelblue"),
                    no = tags$i(class = "fas fa-virus-slash",
                                style = "color: steelblue"))
                ),
                hr(),
                prettyRadioButtons(
                  inputId = "DASelector",
                  label = "Differential Abundance: ", inline = TRUE,
                  selected = "None", choices = daMethods,
                  status = "primary", fill = TRUE
                ),
                sliderInput(
                  inputId = "corSelector",
                  label = "Select a Correlation Cut-off Values",
                  min = C3NAObj$misc$corCut, max = 1, step = 0.05,
                  value = 0.2
                ),
                sliderTextInput(
                  inputId = "nodeBorderWidth",
                  label = "Node Border Color Width: ",  choices = 1:15,
                  selected = 6, grid = TRUE
                ),
                sliderTextInput(
                  inputId = "nodeLabelSize",
                  label = "Node Label Size: ", choices = 1:15,
                  selected = 6, grid = TRUE
                ),
                sliderTextInput(
                  inputId = "edgeLabelSize",
                  label = "Edge Label Size: ", choices = 1:15,
                  selected = 6, grid = TRUE
                ),
                circle = TRUE, status = "success",
                icon = icon("filter"), width = "600px",
                animate = animateOptions(
                  enter = shinyWidgets::animations$bouncing_entrances$bounceInLeft,
                  exit = shinyWidgets::animations$bouncing_exits$bounceOutLeft
                ),
                tooltip = tooltipOptions(title = "Click to change Network Plot Settings")
              ),
              visNetworkOutput("SelectedTaxaPlots",
                               height = "800px")
            ),
            column(
              width = 4,
              box(
                width = 12,
                title = HTML("Selected Taxon Count Distribution", "<font size='3'>", 
                             as.character(actionLink(inputId = "MS_SingelTaxaViolin", 
                                                     label = "", 
                                                     icon = icon("question-circle"))), "</font>"), 
                status = "success",
                height = "31em",
                solidHeader = TRUE,
                plotly::plotlyOutput("countDistributionPlot", height = "380px")

              ),
              box(
                width = 12,
                title = HTML("Sequentially Selected Taxa - Comparison", "<font size='3'>", 
                             as.character(actionLink(inputId = "MS_TwoTaxaViolin", 
                                                     label = "", 
                                                     icon = icon("question-circle"))), "</font>"),
                status = "success",
                height = "31em",
                solidHeader = TRUE,
                plotly::plotlyOutput("countDistributionPlot_Compare", height = "380px")
              )
            )
          ),
          ###### Section 5.1 Empty Space at the Bottom ######
          fluidRow(
            hr()
          )
        )
      ),
      tabPanel(
        ## First Panel
        title = HTML("<b>Panel 2: Download Panel </b>"),
        value = "panel2",
        fluidRow(
          box( ### 5.1 Download Plot List and Button ### 
            width = 6, 
            title = HTML("Download Plots", "<font size='3'>", 
                         as.character(actionLink(inputId = "MS_DownloadPanel", 
                                                 label = "", 
                                                 icon = icon("question-circle"))), "</font>"), 
            status = "primary", solidHeader = TRUE, 
            uiOutput("plotSelector"), 
            hr(),
            ## Conditional Panels 
            conditionalPanel(
              condition = "input.plotRadioButtons=='HTML'",
              fluidRow(
                column(
                  width = 6,
                  textInput(inputId = "heightInput",
                            label = "Height in Pixel",
                            placeholder =  "Height in Pixel",
                            value = "1920"),
                ),
                column(
                  width = 6,
                  textInput(inputId = "widthInput",
                            label = "Width in Pixel",
                            placeholder =  "Width in Pixel",
                            value = "1200"),
                )
              ),
              h6(HTML("Please select the heigth and width for the plot, unit: Pixel. ")), # The default dpi is set to 300.
              hr()
            ),
            radioGroupButtons(
              inputId = "plotRadioButtons",
              label = "Select a format for the plot",
              choices = c("HTML"),
              justified = TRUE,
              checkIcon = list(
                yes = icon("ok", 
                           lib = "glyphicon")),
              status = "custom-status-downloadPlots"
            ), hr(),
            fluidRow(
              column(
                12,
                downloadBttn(
                  outputId = "downloadPlots",
                  label = "Download the Selected Plot", 
                  style = "gradient",
                  color = "primary"
                )
              )
            )
          )
        )
      )
    ),
    
    server = function(input, output, session) {
      # Reactive values
      values <- reactiveValues(
        panel1SelectedIDs = NULL,
        click1 = NULL, click1_TaxaLvl = NULL,
        click2 = NULL, click2_TaxaLvl = NULL,
        fullegend = FALSE, selectedFuncs = NULL
      )
      plots_Plotly <- reactiveValues()
      
      # First Panel
      ###### Section 1 General Stat ###### 
      ### Taxa Related StatTable
      output$taxaRelatedStatTable <- reactable::renderReactable({
          reactable(
              statsTable,
              groupBy = c("Group", "Taxonomic Levels"),
              columns = list(
                  `Taxonomic Levels` = colDef(aggregate = "unique"),
                  `Number of Unique Taxa` = colDef(aggregate = "sum", format = colFormat(separators = TRUE))
              )
          )
      })
      
      ##### Sunburst Disease #####
      output$pheno1Sunburst <- renderPlotly({
        nodeData_Disease = subset(nodesAll, !is.na(ClusterID_Disease)) %>%
          mutate(taxaLevelAbbrev = sapply(strsplit(TaxaName, "_"), "[", 1)) %>%
          mutate(`Taxonomic Levels` = case_when(
            taxaLevelAbbrev =="p" ~ "Phylum",
            taxaLevelAbbrev =="c" ~ "Class",
            taxaLevelAbbrev =="o" ~ "Order",
            taxaLevelAbbrev =="f" ~ "Family",
            taxaLevelAbbrev =="g" ~ "Genus", 
            taxaLevelAbbrev =="s" ~ "Species"
          )) %>% 
          group_by(`Taxonomic Levels`) %>%
          dplyr::summarise(n = n())
        nodeData_Disease$`Taxonomic Levels` = factor(nodeData_Disease$`Taxonomic Levels`, 
                                                     levels = c("Phylum", "Class", "Order", "Family", "Genus", "Species"))
        nodeData_Disease = nodeData_Disease %>% arrange(`Taxonomic Levels`)
        diseaseData = data.frame(
          labels = c(paste0("<span style='font-size: 16'><b>", data1Name, " </b></span><br>nTaxa: ", sum(nodeData_Disease$n)), 
                     as.character(nodeData_Disease$`Taxonomic Levels`)),
          parents = c("", rep(paste0("<span style='font-size: 16'><b>", data1Name, " </b></span><br>nTaxa: ", sum(nodeData_Disease$n)), nrow(nodeData_Disease))),
          values = c(0, nodeData_Disease$n),
          colors = c("#ccaf1d", "#0B701E", "#FF7300", "#120C94", "#db2545", "#009BB3", "#EBEBEB")
        )
        sunburst_Disease = 
          plot_ly(
            diseaseData, 
            labels = ~labels, parents = ~parents, 
            values = ~values,# ids = ~ids, 
            type = "sunburst"
          ) %>%
            layout(colorway  = ~colors,
                   margin = list(l = 0, r = 0, b = 0, t = 0))
        sunburst_Disease$sizingPolicy$padding <- "0"
        plots_Plotly[["sunburst_Disease"]] <- sunburst_Disease
        
        sunburst_Disease
      })
      
      ##### Sunburst Reference #####
      output$pheno2Sunburst <- renderPlotly({
        nodeData_Control = subset(nodesAll, !is.na(ClusterID_Control)) %>%
          mutate(taxaLevelAbbrev = sapply(strsplit(TaxaName, "_"), "[", 1)) %>%
          mutate(`Taxonomic Levels` = case_when(
            taxaLevelAbbrev =="p" ~ "Phylum",
            taxaLevelAbbrev =="c" ~ "Class",
            taxaLevelAbbrev =="o" ~ "Order",
            taxaLevelAbbrev =="f" ~ "Family",
            taxaLevelAbbrev =="g" ~ "Genus", 
            taxaLevelAbbrev =="s" ~ "Species"
          )) %>% 
          group_by(`Taxonomic Levels`) %>%
          dplyr::summarise(n = n())
        nodeData_Control$`Taxonomic Levels` = factor(nodeData_Control$`Taxonomic Levels`, 
                                                     levels = c("Phylum", "Class", "Order", "Family", "Genus", "Species"))
        nodeData_Control = nodeData_Control %>% arrange(`Taxonomic Levels`)
        controlData = data.frame(
          labels = c(paste0("<span style='font-size: 16'><b>", data2Name, " </b></span><br>nTaxa: ", sum(nodeData_Control$n)), 
                     as.character(nodeData_Control$`Taxonomic Levels`)),
          parents = c("", rep(paste0("<span style='font-size: 16'><b>", data2Name, " </b></span><br>nTaxa: ", sum(nodeData_Control$n)), nrow(nodeData_Control))),
          values = c(0, nodeData_Control$n),
          colors = c("#ccaf1d", "#0B701E", "#FF7300", "#120C94", "#db2545", "#009BB3", "#EBEBEB")
        )
        sunburst_Control = 
          plot_ly(
            controlData, 
            labels = ~labels, parents = ~parents, 
            values = ~values,# ids = ~ids, 
            type = "sunburst"
          ) %>%
          layout(colorway  = ~colors,
                 margin = list(l = 0, r = 0, b = 0, t = 0))
        sunburst_Control$sizingPolicy$padding <- "0"
        plots_Plotly[["sunburst_Control"]] <- sunburst_Control
        sunburst_Control
      })
      
      ###### Section 2 Compartive Stat ######
      ###### . 2.1 Module-Module Preservation######
      output$moduleModuleComparison <- renderPlotly({
        df = nodesAll %>%
          mutate(presence = 1) %>%
          pivot_wider(names_from = colors_Control, values_from = presence, values_fn = list(presence = sum), 
                      id_cols = colors_Disease) %>%
          mutate(colors_Disease = ifelse(is.na(colors_Disease), "Not Found", colors_Disease)) %>%
          column_to_rownames("colors_Disease")
        if("NA" %in% colnames(df)){
          colnames(df)[match("NA", colnames(df))] = "Not Found"
        }
        curTitle = paste0(data1Name, " Vs ", data2Name)
        df[is.na(df)] = 0
        df_melt = df %>%
          mutate(colors_Disease = rownames(df)) %>%
          pivot_longer(names_to = "colors_Control", values_to = "count", cols = colnames(df))
        df_melt$text = paste0("<b>", data1Name, " Module Color: </b>", df_melt$colors_Disease, "<br>",
                              "<b>", data2Name, " Module Color: </b>", df_melt$colors_Control, "<br>",
                              "<b>Number of Shared Taxa: </b>", df_melt$count, "<br>")
        df_Wide = df_melt %>%
          pivot_wider(names_from = colors_Control, values_from = text, 
                      id_cols = colors_Disease) %>%
          mutate(colors_Disease = ifelse(is.na(colors_Disease), "Not Found", colors_Disease)) %>%
          column_to_rownames("colors_Disease")
        df_Wide = df_Wide[rownames(df), colnames(df)]
        modModPlot = 
          plot_ly(x = colnames(df), y = rownames(df),
                z = as.matrix(df), 
                hoverinfo = 'text',
                colors=colorRamp(c("#083D7F","#845E40","#E27516")),
                colorbar=list(title="Count",len=1),
                text = as.matrix(df_Wide),
                type = "heatmap") %>%
          layout(
            title = paste0(curTitle, " Module Comparison"),
            xaxis = list(
              title = data2Name,
              showspikes = TRUE,
              spikethickness = 1
            ),
            yaxis = list(
              title = data1Name,
              showspikes = TRUE,
              spikethickness = 1,
              spikedash = "10px"
            ),
            hovermode  = 'x'
          ) %>%
          add_annotations(x = df_melt$colors_Control,
                          y = df_melt$colors_Disease,
                          text = df_melt$count, 
                          showarrow = FALSE, 
                          font = list(
                            color = "white"
                          ),
                          ax = 20,
                          ay = -20)
        plots_Plotly[["modModPlot"]] <- modModPlot
        modModPlot
      })
      
      ###### . 2.2 Module Preservation Table ###### 
      output$modulePreservation <- renderPlotly({
        modulePlotData_Wide$text = paste0("<b>Module Color: </b>", modulePlotData_Wide$color, "<br>",
                                          "<b>Module Size: </b>", modulePlotData_Wide$moduleSize)
        modPreservationPlot =
          plot_ly(data = modulePlotData_Wide,
                x = ~ZSummary, y = ~medianRank,
                
                marker = list(line = list(color = "#0E1012"),
                              size = ~moduleSize,
                              color = ~color, 
                              colors = ~color),
                type = "scatter", hovertemplate = ~text) %>%
          layout(
            shapes = list(
              ## Preserved Region ZSummary < 10 and median rank > 8 
              list(type = "rect",
                   fillcolor = "#F3C6C9", 
                   line = list(color = "#B59395"), 
                   opacity = 0.3,
                   x0 = 5, 
                   x1 = max(C3NAObj$modulePreservation$modulePlotData_Wide$ZSummary
                            [which(is.finite(C3NAObj$modulePreservation$modulePlotData_Wide$ZSummary))])+2, 
                   xref = "x",
                   y0 = 0, y1 = 8, yref = "y"),
              ## Non Preserved Region ZSummary < 10 and median rank > 8 
              list(type = "rect",
                   fillcolor = "#F0EBB9", 
                   line = list(color = "#9AB2B8"),
                   opacity = 0.3,
                   x0 = 0, x1 = 10, xref = "x",
                   y0 = 8, 
                   y1 = max(C3NAObj$modulePreservation$modulePlotData_Wide$medianRank
                            [which(is.finite(C3NAObj$modulePreservation$modulePlotData_Wide$medianRank))])+2, 
                   yref = "y"),
              ## Intermediately Preserved Region 
              list(type = "rect",
                   fillcolor = "#CEEDF5", 
                   line = list(color = "#9AADB8"),
                   opacity = 0.3,
                   x0 = 10, 
                   x1 = max(C3NAObj$modulePreservation$modulePlotData_Wide$ZSummary
                            [which(is.finite(C3NAObj$modulePreservation$modulePlotData_Wide$ZSummary))])+2, 
                   xref = "x",
                   y0 = 8, 
                   y1 = max(C3NAObj$modulePreservation$modulePlotData_Wide$medianRank
                            [which(is.finite(C3NAObj$modulePreservation$modulePlotData_Wide$medianRank))])+2, 
                   yref = "y")
            ),
            annotations = list(
              list(
                x = 0.02,
                y = 0.975,
                text = "Non-Preserved",
                xref = "paper", yref = "paper",
                showarrow = FALSE, 
                font = list(color = "#A3A26D", size = 13)
              ),
              list(
                x = 0.975,
                y = 0.025,
                text = "Strongly Preserved",
                xref = "paper", yref = "paper",
                showarrow = FALSE, 
                font = list(color = "#A65358", size = 13)
              ),
              list(
                x = 0.975,
                y = 0.975,
                text = "Intermediately Preserved",
                xref = "paper", yref = "paper",
                showarrow = FALSE, 
                font = list(color = "#676D99", size = 13)
              )
            ), 
            xaxis = list(title = "<i>Z<sub>Summary</sub></i>"),
            yaxis = list(title = "<i>medianRank</i>")
          )
        
        plots_Plotly[["modPreservationPlot"]] <- modPreservationPlot
        modPreservationPlot
      })
      
      ###### . 2.3 Click Taxa ######
      output$clickedModuleInfo <- DT::renderDataTable({
        tmp <- event_data("plotly_click", priority = "event")
        if(is.numeric(tmp$x)){
          closestZSum = which.min(abs(modulePlotData_Wide$ZSummary-tmp$x))
          matchedZSum = modulePlotData_Wide$ZSummary[closestZSum]
          closestMedRank = which.min(abs(modulePlotData_Wide$medianRank-tmp$y))
          matchedMedRank = modulePlotData_Wide$medianRank[closestMedRank]
          tempMod = subset(modulePlotData_Wide, ZSummary == matchedZSum & medianRank == matchedMedRank)
        } else {
          tempMod = data.frame()
        }
        ## Only react to Module Preservation plot
        if("x" %in% colnames(tmp)){
          if(nrow(tempMod)>0){
            selectedColor = nodesAll %>%
              filter(colors_Control == tempMod$color[1]) %>%
              dplyr::select(TaxaName, colors_Disease, colors_Control) %>%
              dplyr::rename("Comparison Color" = colors_Disease,
                            "Reference Color" = colors_Control)
            DT::datatable(selectedColor,
                          options = list(
                            rownames = FALSE,
                            autoWidth = TRUE,
                            searching = TRUE,
                            pageLength = 7
                          ))
          } else {
            placeholderData = data.frame(
              "Instruction" = paste0("Please click on the Module in the \n", 
                                     "'Module Preservation Statistics' plot\n to ", 
                                     "display the members of the module, which is \n", 
                                     "based on the modules from Phenotype: '", data2Name, 
                                     "'. (Left Figure)")
            )
            rownames(placeholderData) = ""
            datatable(
              placeholderData,              
              rownames = FALSE,
              options = list(
                dom = 't',
                autoWidth = TRUE,
                searching = FALSE,
                pageLength = 1
              )
            )
          }
        } else {
          placeholderData = data.frame(
            "Instruction" = paste0("Please click on the Module in the \n", 
                                   "'Module Preservation Statistics' plot\n to ", 
                                   "display the members of the module, which is \n", 
                                   "based on the modules from Phenotype: '", data2Name, 
                                   "'. (Left Figure)")
          )
          rownames(placeholderData) = ""
          datatable(
            placeholderData,              
            rownames = FALSE,
            options = list(
              dom = 't',
              autoWidth = TRUE,
              searching = FALSE,
              pageLength = 1
            )
          )
        }

      })
      
      ###### Section 3 Extract Specific Network ######
      ###### . 3.1 Taxa Selector ###### 
      output$taxaSelector <- renderUI({
        multiInput(
          inputId = "taxaSelector",
          label = "Selected Taxa:", 
          choices = NULL, 
          choiceNames = nodesAll$TaxaName, 
          choiceValues = nodesAll$TaxaName
        )
      })
      
      ###### . 3.2 Intra Modular Corr Table ######
      observeEvent(input$taxaSelector, {
        matchedID = input$taxaSelector
        values[["panel1SelectedIDs"]] = matchedID
        
        updatePickerInput(
          session, 
          inputId = "barPlotTaxaSelector",
          choices = c("All", matchedID)
        )
        
        updatePickerInput(
          session, 
          inputId = "taxaModularTaxaSelector",
          choices = c(matchedID)
        )
      })

      output$intramodularCorTable <- DT::renderDataTable({
        if(length(input$taxaSelector) > 0){
          matchedID = input$taxaModularTaxaSelector
          if(length(matchedID) > 0){
            outputDataTable = subset(sparccP_Filtered_rbind, source %in% matchedID | target %in% matchedID) %>%
              filter(cor >= corCut & fdr <= fdr) %>%
              group_by(Diagnosis, clusterID) %>% filter(clusterID != "Inter-Modular") %>%
              left_join(nodesAbbre, by = c("clusterID" = "ClusterID")) %>% ungroup() %>%
              rename(Colors = colors) %>%
              group_by(Diagnosis, Colors) %>%
              dplyr::summarise(n = n()) %>%
              dplyr::rename("Number of Important Correlations" = "n")
            if(nrow(outputDataTable) > 0){
              datatable(
                outputDataTable,
                rownames = FALSE, 
                options = list(
                  autoWidth = TRUE,
                  searching = FALSE,
                  pageLength = 7
                )
              )
            } else {
              placeholderData = data.frame(
                "Information" = paste0("No Significant Taxa-Taxa Correlations Found. ", 
                                                     "Please select another taxon.")
              )
              rownames(placeholderData) = ""
              datatable(
                placeholderData,              
                rownames = FALSE,
                options = list(
                  dom = 't',
                  autoWidth = TRUE,
                  searching = FALSE,
                  pageLength = 1
                )
              )
            }
          }
        }else {
          placeholderData = data.frame(
            "Information" = paste0("No taxon selected from the 'Taxa Selector' on the Left. ")
          )
          rownames(placeholderData) = ""
          datatable(
            placeholderData,              
            rownames = FALSE,
            options = list(
              dom = 't',
              autoWidth = TRUE,
              searching = FALSE,
              pageLength = 1
            )
          )
        }
      })
          
      output$barPlotTaxaSelector <- renderUI({
        if(is.null(values[["panel1SelectedIDs"]])){
          pickerInput(
            inputId = "barPlotTaxaSelector",
            label = "Taxa Selector:", 
            choices = c("All"), 
            selected = "All"
          )
        } else {
          matchIDs = values[["panel1SelectedIDs"]]
          pickerInput(
            inputId = "barPlotTaxaSelector",
            label = "Taxa Selector:", 
            choices = c("All", matchIDs), 
            selected = "All"
          )
        }
      })
      
      output$taxaModularTaxaSelector <- renderUI({
        if(is.null(values[["panel1SelectedIDs"]])){
          pickerInput(
            inputId = "taxaModularTaxaSelector",
            label = "Taxa Selector:", 
            choices = NULL
          )
        } else {
          matchIDs = values[["panel1SelectedIDs"]]
          pickerInput(
            inputId = "taxaModularTaxaSelector",
            label = "Taxa Selector:", 
            choices = matchIDs, selected = matchIDs[1]
          )
        }
      })
      
      ###### . 3.3 Bar Plot ######
      observeEvent(input$barPlotTaxaSelector, {
        req(input$barPlotPhenoSelector)
        output$barPlot <- renderPlotly({
          if(input$barPlotTaxaSelector == "All"){
            nodesAll_Pheno = nodesAll_PhenoOri
            nodesAll_Pheno$`Taxonomic Levels` = 
              factor(nodesAll_Pheno$`Taxonomic Levels`,
                     levels = c("Phylum", "Class", "Order", "Family", "Genus", 
                                "Species", "Unselected"))
            if(input$barPlotPhenoSelector == phenotypes[1]){
              curPheno = "ClusterID_Disease"
            } else{
              curPheno = "ClusterID_Control"
            }
            nodesAll_Pheno$`Cluster ID` = nodesAll_Pheno[, curPheno]
            barPlotSelected =
              plot_ly(nodesAll_Pheno, 
                    x = ~`Cluster ID`,
                    y = ~`Number of Unique Taxa`,
                    type = "bar", 
                    hovertext = ~description,
                    color = ~`Taxonomic Levels`,
                    colors = c("#ccaf1d", "#0B701E", "#FF7300", "#120C94", 
                               "#db2545", "#009BB3", "#EBEBEB"),
                    marker = list(line = list(color = '#565656',
                                              width = 1))
              ) %>%
              layout(
                barmode = "relative",
                xaxis = list(title = paste0("Module ID from Phenotype: ", 
                                            input$barPlotPhenoSelector)),
                yaxis = list(title = "Number of Unique Taxa per Module")
              )
            plots_Plotly[["barPlotAll"]] <- barPlotSelected
            barPlotSelected
          } else {
            nodesAll_Pheno = nodesAll_PhenoOri
            matchedID = input$barPlotTaxaSelector
            matchedTaxonomicLevel = subset(nodesAll_Pheno, TaxaName == matchedID)$`Taxonomic Levels`

            relatedTaxaName = subset(nodesAll_Pheno, TaxaName == matchedID)
            nodesAll_Pheno$`Taxonomic Levels` = ifelse(nodesAll_Pheno[, matchedTaxonomicLevel] == matchedID,
                                                       as.character(nodesAll_Pheno$`Taxonomic Levels`), "Unselected")
            nodesAll_Pheno$`Taxonomic Levels` = 
              factor(nodesAll_Pheno$`Taxonomic Levels`,
                     levels = c("Phylum", "Class", "Order", "Family", "Genus", 
                                "Species", "Unselected"))
            if(input$barPlotPhenoSelector == phenotypes[1]){
              curPheno = "ClusterID_Disease"
            } else{
              curPheno = "ClusterID_Control"
            }
            nodesAll_Pheno$`Cluster ID` = nodesAll_Pheno[, curPheno]

            barPlotAll = 
              plot_ly(nodesAll_Pheno, 
                    x = ~`Cluster ID`,
                    y = ~`Number of Unique Taxa`,
                    type = "bar", 
                    hovertext = ~description,
                    color = ~`Taxonomic Levels`,
                    colors = c("#ccaf1d", "#0B701E", "#FF7300", "#120C94", 
                               "#db2545", "#009BB3", "#EBEBEB"),
                    marker = list(line = list(color = '#565656',
                                              width = 1))
              ) %>%
                layout(
                  barmode = "relative",
                  xaxis = list(title = paste0("Module ID from Phenotype: ", 
                                              input$barPlotPhenoSelector)),
                  yaxis = list(title = "Number of Unique Taxa per Module")
                )
            plots_Plotly[["barPlotAll"]] <- barPlotAll
            barPlotAll
          }
        })
      })
      
      ## Bar plot placeholder
      output$barPlot <- renderPlotly({
        plotly_empty(type = "scatter", mode = "markers") %>%
          config(
            displayModeBar = FALSE
          ) %>%
          layout(
            title = list(
              text = paste0("Please click on the filter button on the top left."),
              yref = "paper",
              y = 0.5
            )
          )
      })
      
      ##### Section 4 Network Plots and Individual Violin Plot #####
      networkMultiListener <- reactive({
        list(input$refreshPanel1Section3, input$functionSelector)
      })
      
      ##### . 4.1 Network Plot #####
      observeEvent(networkMultiListener(), {
        req(input$nodesMode)
        req(input$corMode)
        req(input$nodeBorderWidth)
        req(input$nodeLabelSize)
        req(input$edgeLabelSize)
        req(input$samePhylo)
        req(input$DASelector)
        req(input$corSelector)
        
        output$SelectedTaxaPlots = visNetwork::renderVisNetwork({
          ## Obtain all the matched ID within 
          curCorMode = input$corMode
          curNodeBorderWidth = input$nodeBorderWidth
          curNodeLabelSize = input$nodeLabelSize
          curEdgeLabelSize = input$edgeLabelSize
          curTaxaLvls = input$taxaLvls
          cursamePhylo = input$samePhylo
          curCorFilter = input$corSelector
          
          if(!is.null(values[["panel1SelectedIDs"]])){
            if(input$nodesMode == "selectedTaxa"){
              relatedTaxa = values[["panel1SelectedIDs"]]
            } else if (input$nodesMode == "relatedTaxa"){
              corCut = curCorFilter
              matchedID = values[["panel1SelectedIDs"]]
              filteredData = subset(sparccP_Filtered_rbind, source %in% matchedID | target %in% matchedID) %>%
                filter(cor >= corCut) %>% filter(Module != "Inter-Modular") 
              clusterIDsDisease = subset(filteredData, Diagnosis == data1Name) %>% pull(clusterID) %>% unique()
              clusterIDsControl = subset(filteredData, Diagnosis == data2Name) %>% pull(clusterID) %>% unique()
              filteredData_Disease = subset(filteredData, Diagnosis == data1Name & clusterID %in% clusterIDsDisease)
              filteredData_Control = subset(filteredData, Diagnosis == data2Name & clusterID %in% clusterIDsControl)
              
              relatedTaxa = unique(c(filteredData_Disease$source, 
                                     filteredData_Disease$target, 
                                     filteredData_Control$source, 
                                     filteredData_Control$target))
            } else {
              matchedID = values[["panel1SelectedIDs"]]
              filteredData = subset(sparccP_Filtered_rbind, source %in% matchedID | target %in% matchedID) %>%
                filter(cor >= corCut) %>% filter(Module != "Inter-Modular") 
              clusterIDsDisease = subset(filteredData, Diagnosis == data1Name) %>% pull(clusterID) %>% unique()
              clusterIDsControl = subset(filteredData, Diagnosis == data2Name) %>% pull(clusterID) %>% unique()
              filteredData_Disease = subset(sparccP_Filtered_rbind, Diagnosis == data1Name & clusterID %in% clusterIDsDisease) %>%
                filter(cor >= corCut)
              filteredData_Control = subset(sparccP_Filtered_rbind, Diagnosis == data2Name & clusterID %in% clusterIDsControl) %>%
                filter(cor >= corCut)
              relatedTaxa = unique(c(filteredData_Disease$source, 
                                     filteredData_Disease$target, 
                                     filteredData_Control$source, 
                                     filteredData_Control$target))
            }
            
            if(input$nodesMode == "selectedTaxa"){
              sparccP_Filtered_Combined_Filtered = subset(sparccP_Filtered_Combined, source %in% relatedTaxa | target %in% relatedTaxa) %>% 
                filter(cor_Disease >= corCut | cor_Control >= corCut) %>%
                filter(!is.na(clusterID_Disease) | !is.na(clusterID_Control)) %>%
                filter(!(source %in% removedTaxaTable$removedTaxa)) %>%
                filter(!(target %in% removedTaxaTable$removedTaxa)) 
            } else{
              sparccP_Filtered_Combined_Filtered = subset(sparccP_Filtered_Combined, source %in% relatedTaxa & target %in% relatedTaxa) %>% 
                filter(cor_Disease >= corCut | cor_Control >= corCut) %>%
                filter(!is.na(clusterID_Disease) | !is.na(clusterID_Control)) %>%
                filter(!(source %in% removedTaxaTable$removedTaxa)) %>%
                filter(!(target %in% removedTaxaTable$removedTaxa)) 
            }

            ## Calculate Edge and Nodes Table
            nodesTable = nodesAll %>%
              filter(TaxaName %in% relatedTaxa) %>%
              mutate(color.background = colors_Disease, 
                     color.border = colors_Control,
                     borderWidth = as.integer(curNodeBorderWidth),
                     id = TaxaName,
                     label = TaxaName,
                     font.size = as.integer(curNodeLabelSize),
                     shape = "dot"
              ) %>%
              mutate(shape = ifelse(TaxaName %in% values[["panel1SelectedIDs"]], "star", shape)) %>%
              mutate(title = paste0("TaxaName: ", TaxaName, "<br><hr>",
                                    "Disease Type: ", data1Name, "<br>",
                                    "Disease Color Module: ", colors_Disease, "<br><hr>",
                                    "Control Type: ", data2Name, "<br>",
                                    "Control Color Module: ", colors_Control, "<br><hr>"
                                    ),
                     size = 10
                     ) %>%
              mutate(color.background = ifelse(color.background == "grey60", "#808080", color.background),
                     color.border = ifelse(color.border == "grey60", "#808080", color.border))
            if(!is.null(C3NAObj$C3NA_Wilcoxon)){
              nodesTable = nodesTable %>%
                left_join(C3NAResults, 
                        by = "TaxaName", all.x = TRUE)
            }

            ## Filter Vertices based on taxonomic levels
            nodesTable = nodesTable %>%
              rowwise() %>%
              mutate(taxaLvls_Abbre = substring(TaxaName, 1, 1)) %>%
              filter(taxaLvls_Abbre %in% curTaxaLvls) %>%
              group_by(TaxaName) %>% filter(row_number() == 1)
            
            ## Adding Differential Abundance Analysis Results if Available
            ###### !!!!!!DA  ######
            if(length(daMethods) > 1 & input$DASelector != "None"){
              daData_temp = daData[, c("TaxaName", input$DASelector)]
              colnames(daData_temp)[2] = "DA"
              nodesTable = nodesTable %>%
                left_join(daData_temp, 
                          by = "TaxaName", all.x = TRUE) %>%
                as.data.frame() %>%
                mutate(DA = as.character(DA)) %>%
                mutate(DA = ifelse(is.na(DA), FALSE, DA)) %>%
                mutate(title = paste0(title, 
                                      "Differential Abundance: ", DA, "<br><hr>"), 
                       shape = ifelse(DA == TRUE, "square", shape)) %>%
                replace(is.na(.), values = FALSE)
              lnodes <- data.frame(
                label = c("DA", "Selected", "Others"),
                shape = c("square", "star", "dot"),
                shadow = c(FALSE, FALSE, FALSE)
              )
            } else {
              lnodes <- data.frame(
                label = c("Selected", "Others"),
                shape = c("star", "dot"),
                shadow = c(FALSE, FALSE)
              )
            }
            
            ## C3NA Results
            if(!is.null(C3NAObj$C3NA_Wilcoxon)){
              nodesTable = nodesTable %>%
                mutate(C3NA = ifelse(is.na(C3NA), FALSE, C3NA)) %>%
                mutate(title = paste0(title, 
                                      "C3NA Wilcoxon: ", C3NA, ""), 
                       shape = ifelse(C3NA == TRUE, "triangle", shape)) 
              
              ## Update Legend
              selectedFuncs = values[["selectedFuncs"]]
              avaliableShape = c("diamond", "triangle", "ellipse")
              lnodesV2 <- data.frame(
                label = "C3NA",
                shape = "triangle",
                shadow = FALSE
              )
              lnodes = rbind(lnodesV2, lnodes)
            } else {
              lnodes = lnodes %>%
                filter(!(shape %in% c("triangle")))
            }
            
            ## Remove Duplicates Caused by inputTaxaName
            if("inputTaxaName" %in% colnames(nodesTable)){
              nodesTable = nodesTable %>%
                select(-inputTaxaName) %>%
                distinct()
            }
            
            # Check if both C3NA & DA 
            if(!is.null(C3NAObj$C3NA_Wilcoxon) & (length(daMethods) > 1 & input$DASelector != "None")){
              nodesTable = nodesTable %>%
                mutate(shape = ifelse(C3NA == TRUE & DA == TRUE, "diamond", shape))
              
              lnodesV2 <- data.frame(
                label = "Both",
                shape = "diamond",
                shadow = FALSE
              )
              lnodes = rbind(lnodesV2, lnodes)
            }
            
            ledges <- data.frame(
              label = c("Disease-Only", "Control-Only", "Shared"),
              color = c( "#77BBEB", "#EB7874", "#CCCFCB"),
              dashes = c(FALSE, FALSE, FALSE),
              arrows.from.type = c("dot", "dot", "dot"),
              arrows.to.type = c(NA, NA, NA),
              font.align = "top"
            )
            
            # Edge
            edgesTable = sparccP_Filtered_Combined_Filtered %>%
              filter(cor_Disease >= corCut | cor_Control >= corCut) %>%
              mutate(cor_Disease_Filtered = ifelse(cor_Disease < corCut | is.na(cor_Disease) | Module_Disease == "Inter-Modular" | is.na(Module_Disease), 0, cor_Disease),
                     cor_Control_Filtered = ifelse(cor_Control < corCut | is.na(cor_Control) | Module_Control == "Inter-Modular" | is.na(Module_Control), 0, cor_Control)) %>%
              mutate(from = source, 
                     to = target,
                     color = case_when(
                       (Module_Control=="Inter-Modular" | is.na(Module_Control)) & Module_Disease=="Intra-Modular" ~ "#77BBEB70",
                       (Module_Disease=="Inter-Modular" | is.na(Module_Disease)) & Module_Control=="Intra-Modular" ~ "#EB787470",
                       (Module_Disease=="Intra-Modular" & Module_Control=="Intra-Modular") ~ "#CCCFCB70"
                     )                          ,
                     label = case_when(
                       (Module_Disease=="Intra-Modular" & Module_Control=="Intra-Modular") ~ paste0(round(cor_Disease_Filtered, 2), " | ", round(cor_Control_Filtered, 2)),
                       (Module_Control=="Inter-Modular" | is.na(Module_Control)) & Module_Disease=="Intra-Modular" ~ as.character(round(cor_Disease_Filtered, 2)),
                       (Module_Disease=="Inter-Modular" | is.na(Module_Disease)) & Module_Control=="Intra-Modular" ~ as.character(round(cor_Control_Filtered, 2))
                     ),
                     font.color = case_when(
                       (Module_Disease=="Intra-Modular" & Module_Control=="Intra-Modular") ~ "#CCCFCB85",
                       (Module_Control=="Inter-Modular" | is.na(Module_Control)) & Module_Disease=="Intra-Modular" ~ "#77BBEB85",
                       (Module_Disease=="Inter-Modular" | is.na(Module_Disease)) & Module_Control=="Intra-Modular" ~ "#EB787485"
                     )
              ) %>%
              filter(!(cor_Disease <= corCut & Module_Control == "Inter-Modular") & 
                       !(cor_Control <= corCut & Module_Disease == "Inter-Modular")) %>%
              mutate(font.size = as.integer(curEdgeLabelSize)) %>%
              left_join(consensusProp_Disease, by = "taxaComboName", all.x = TRUE) %>%
              left_join(consensusProp_Control , by = "taxaComboName", all.x = TRUE) %>%
              mutate(title = paste0("Connection between: <br>", from, " & ", to, "<hr>",
                                    "Disease Consensus:    ", DiseaseConsensusProp, "<br>", 
                                    "Control Consensus:    ", ControlConsensusProp, "<br><hr>",
                                    "Disease Cor:    ", round(cor_Disease,3), "<br>", 
                                    "Control Cor:    ", round(cor_Control,3), "<br><hr>",
                                    "Disease Module: ", Module_Disease, "<br>",
                                    "Control Module: ", Module_Control)) %>%
              left_join(samePhyloTable, by = "taxaComboName", all.x = TRUE) %>%
              mutate(samePhylo = ifelse(is.na(samePhylo), FALSE, samePhylo))
            
            ## Re-format based on Current mode
            if(length(curCorMode) < 3){
              if(!("diseaseOnly" %in% curCorMode)){
                ## Update Edges
                edgesTable = edgesTable %>%
                  filter(color != "#77BBEB70")
                ## Update Nodes
                nodesTable = nodesTable %>%
                  filter(TaxaName %in% unique(unlist(edgesTable[, c("source", "target")])))
              }
              if(!("controlOnly" %in% curCorMode)){
                ## Update Edges
                edgesTable = edgesTable %>%
                  filter(color != "#EB787470")
                ## Update Nodes
                nodesTable = nodesTable %>%
                  filter(TaxaName %in% unique(unlist(edgesTable[, c("source", "target")])))
              }
              if(!("diseaseOnly" %in% curCorMode)){
                ## Update Edges
                edgesTable = edgesTable %>%
                  filter(color != "#EB787470")
                ## Update Nodes
                nodesTable = nodesTable %>%
                  filter(TaxaName %in% unique(unlist(edgesTable[, c("source", "target")])))
              }
            }
            
            if(cursamePhylo == "Remove"){
              edgesTable$samePhyloV2 = ifelse(is.na(edgesTable$samePhylo), FALSE, TRUE)
              edgesTable = edgesTable %>% dplyr::filter(samePhyloV2 != TRUE)
            }
            values[["nodesTable"]] = nodesTable
            
            networkPlot = 
              visNetwork(nodes = nodesTable, edges = edgesTable
              ) %>%
                visNodes(
                  shadow = list(enabled = FALSE)
                ) %>%
                visOptions(highlightNearest = TRUE) %>%
                visInteraction(navigationButtons = TRUE) %>%
                visEdges(smooth = FALSE) %>%
                visLegend(width=0.1, addEdges = ledges,
                          useGroups = FALSE, addNodes = lnodes,
                          zoom = FALSE, main = "Legend") %>% 
                visPhysics(solver = "forceAtlas2Based", 
                           forceAtlas2Based = list(avoidOverlap = 1, 
                                                   centralGravity = 0.001),
                           minVelocity = 20) %>%
                visEvents(click = "function(nodes){
                                Shiny.onInputChange('click', nodes.nodes[0]);
                                Shiny.onInputChange('node_selected', nodes.nodes.length);
                            ;}"
                ) 
            plots_Plotly[["networkPlot"]] <- networkPlot
            networkPlot
          }
        })
      })
  
      ##### . 4.2 Violin Click Plot #####
      observeEvent(input$click, {
        selectedID = input$click
        taxaLvls <- c("Phylum", "Class", "Order", "Family", "Genus", "Species")
        taxaHeaders <- c("p", "c", "o", "f", "g", "s")
        splitName = strsplit(selectedID, "_") 
        selectedTaxaName = selectedID
        selectedTaxaLvl = taxaLvls[match(splitName[[1]][1], taxaHeaders)]
        selectedTaxaNameFull = gsub(paste0("^", splitName[[1]][1], "_"), "", selectedTaxaName)
        curDiseaseCount = data.frame(
          count = as.numeric(curDisease_OriCountTable[selectedTaxaNam=e, ]),
          sampleID = colnames(curDisease_OriCountTable[selectedTaxaName, ]),
          condition = data1Name
        )
        curControlCount = data.frame(
          count = as.numeric(curControl_OriCountTable[selectedTaxaName, ]),
          sampleID = colnames(curControl_OriCountTable[selectedTaxaName, ]),
          condition = data2Name
        )

        combinedCountTable = rbind(curControlCount, curDiseaseCount)
        combinedCountTable$hoverText = paste0("<b>Sample ID: </b>", combinedCountTable$sampleID, "<br>",
                                              "<b>Count: </b>", combinedCountTable$count, "<br>")

        output$countDistributionPlot <- renderPlotly({
          singleTaxonPlot = suppressWarnings(
            plot_ly(data = combinedCountTable, 
                    x = ~condition, 
                    y = ~log(count), 
                    text  = ~hoverText,
                    points = "all", 
                    split = ~condition,
                    line = list(color = 'rgb(7,40,89)'),
                    marker = list(line = list(color = 'rgb(7,40,89)',
                                              width = 2)),
                    
                    type = 'violin',
                    box = list(
                      visible = T
                    ),
                    meanline = list(
                      visible = T
                    )) %>%
              layout(
                xaxis = list(title="<b>Condition</b>"), 
                yaxis = list(title="<b>Log<sub>2</sub>-Transformed Count</b>"),
                title = paste0(selectedTaxaLvl, " | ", selectedTaxaNameFull)
              )
          )
          plots_Plotly[["singleTaxonPlot"]] <- singleTaxonPlot
          suppressWarnings(singleTaxonPlot)
        })

        
        if(is.null(values[["click1"]]) & is.null(values[["click2"]])){
          values[["click1"]] = selectedTaxaName
        } else if (!is.null(values[["click1"]]) & is.null(values[["click2"]])){
          if(selectedTaxaName != values[["click1"]]){
            values[["click2"]] = selectedTaxaName
          }
        } else if (!is.null(values[["click1"]]) & !is.null(values[["click2"]])){
          if(values[["click1"]] != values[["click2"]] &
             values[["click1"]] != selectedTaxaName &
             values[["click2"]] != selectedTaxaName){
            values[["click1"]] = values[["click2"]]
            values[["click2"]] = selectedTaxaName
          }
        }

        if(!is.null(values[["click1"]]) & !is.null(values[["click2"]])){
          taxaName1 = values[["click1"]]
          taxaName2 = values[["click2"]]
          curControlCount1 = data.frame(
            count = as.numeric(curControl_OriCountTable[values[["click1"]], ]),
            sampleID = colnames(curControl_OriCountTable[values[["click1"]], ]),
            condition = data2Name,
            taxaName = taxaName1
          )
          curDiseaseCount1 = data.frame(
            count = as.numeric(curDisease_OriCountTable[values[["click1"]], ]),
            sampleID = colnames(curDisease_OriCountTable[values[["click1"]], ]),
            condition = data1Name,
            taxaName = taxaName1
          )
          combinedCountTable2 = rbind(curControlCount1, curDiseaseCount1)
          
          curControlCount2 = data.frame(
            count = as.numeric(curControl_OriCountTable[values[["click2"]], ]),
            sampleID = colnames(curControl_OriCountTable[values[["click2"]], ]),
            condition = data2Name,
            taxaName = taxaName2
          )
          curDiseaseCount2 = data.frame(
            count = as.numeric(curDisease_OriCountTable[values[["click2"]], ]),
            sampleID = colnames(curDisease_OriCountTable[values[["click2"]], ]),
            condition = data1Name,
            taxaName = taxaName2
          )
          combinedCountTable2 = rbind(combinedCountTable2, curControlCount2)
          combinedCountTable2 = rbind(combinedCountTable2, curDiseaseCount2)
          
          combinedCountTable2$hoverText = paste0("<b>Sample ID: </b>", combinedCountTable2$sampleID, "<br>",
                                                 "<b>Count: </b>", combinedCountTable2$count, "<br>")
          output$countDistributionPlot_Compare <- renderPlotly({
            fig <- plot_ly(type = 'violin')
            conditions = unique(combinedCountTable2$condition)
            taxaNames = c(values[["click2"]], values[["click1"]])
            df = combinedCountTable2
            showLegend <- c(T,F)
            
            for(i in seq(2)){
              fig <- add_trace(
                fig,
                x = df$taxaName[df$taxaName == taxaNames[i] & df$condition == conditions[1]],
                y = log(df$count[df$taxaName == taxaNames[i] & df$condition == conditions[1]]),
                text = df$hoverText[df$taxaName == taxaNames[i] & df$condition == conditions[1]],
                legendgroup = conditions[1],
                scalegroup = conditions[1],
                name = conditions[1],
                side = 'negative',
                box = list(
                  visible = T
                ),
                points = 'all',
                pointpos = -0.4,
                scalemode = 'count',
                meanline = list(
                  visible = T
                ),
                color = I("#B33A3B"),
                marker = list(
                  line = list(
                    width = 2,
                    color = "#B33A3B"
                  )
                ),
                showlegend = showLegend[i]
              )
              fig <- fig %>%
                add_trace(
                  x = df$taxaName[df$taxaName == taxaNames[i] & df$condition == conditions[2]],
                  y = log(df$count[df$taxaName == taxaNames[i] & df$condition == conditions[2]]),
                  text = df$hoverText[df$taxaName == taxaNames[i] & df$condition == conditions[2]],
                  legendgroup = conditions[2],
                  scalegroup = conditions[2],
                  name = conditions[2],
                  side = 'positive',
                  box = list(
                    visible = T
                  ),
                  points = 'all',
                  pointpos = 0.4,
                  scalemode = 'count',
                  meanline = list(
                    visible = T
                  ),
                  color = I("#2688B3"),
                  marker = list(
                    line = list(
                      width = 2,
                      color = "#2688B3"
                    )
                  ),
                  showlegend = showLegend[i]
                )
            }
            fig <- suppressWarnings(layout(
              fig,
              xaxis = list(title="<b>Condition</b>"),
              yaxis = list(title="<b>Log<sub>2</sub>-Transformed Count</b>"),
              # violingap = 0,
              # violingroupgap = 0,
              # violinmode = 'overlay',
              legend = list(
                tracegroupgap = 0
              )
            ))
            
            plots_Plotly[["twoTaxaPlot"]] <- fig
            suppressWarnings(fig)
          })
        }
      })
      
      ##### 5.0 Download Panel ###### 
      output$plotSelector <- renderUI({
        if(input$plotRadioButtons %in% c("HTML")){
          ## Obtain the list of plots available
          avaliablePlots <- sort(names(plots_Plotly))
          listOfImportantData <- data.frame(
            original = c("sunburst_Disease", "sunburst_Control",   
                         "modModPlot", "modPreservationPlot", 
                         "barPlotAll", "networkPlot",        
                         "singleTaxonPlot", "twoTaxaPlot"),
            full = c("Comparison Sunburst Plot", "Reference Sunburst Plot", 
                     "Module Membership Comparison Plot", "Module Preservation Statistics Plot",
                     "Phenotype-Based Bar Plot", "Network Plot", 
                     "Single Taxon Violin Plot", "Two Taxa Vilin Plot"
                     ))
          checker <- avaliablePlots %in% listOfImportantData$original
          tempData <- avaliablePlots[checker]
          listData <- listOfImportantData %>%
            filter(original %in% tempData)
          
          ## Obtain the list of plots available
          if(nrow(listData) < 1){
            h4("No Plot was generated from Panel 1. Please check your data.")
          } else {
            radioGroupButtons(
              inputId = "plotSelector",
              choiceNames = listData$full, 
              choiceValues = listData$original,
              individual = TRUE,
              checkIcon = list(
                yes = tags$i(class = "fa fa-check-square", 
                             style = "color: white"),
                no = tags$i(class = "fa fa-square-o", 
                            style = "color: black")),
              status = "custom-status-downloadPlots"
            )
          }
        }
      })
      
      ## Observe the download button and handle it
      selectedPlotInput <- function(){
        req(input$plotSelector)
        plots_Plotly[[input$plotSelector]]
      }
      
      # create filename
      selectedPlotName <- reactive({
        name <- input$plotSelector
        if(input$plotRadioButtons == "HTML")   filename <- paste0(name, "-", Sys.Date(), ".html",  sep="")
        return(filename)
      })
      
      output$downloadPlots <- downloadHandler(
        filename = selectedPlotName,
        content = function(file) {
          name <- input$plotSelector
          
          if(input$plotRadioButtons == "HTML") {
            savedData <- selectedPlotInput()
            savedData$height = as.numeric(input$heightInput)
            savedData$width = as.numeric(input$widthInput)
            saveWidget(widget = savedData, file = file)
          }}
      )
      
      ##### 6.0 All Showmodels ######
      observeEvent(input$MS_Pheno1, {
        showModal(
          modalDialog(
            title = HTML('<b>Peek of the Avaliable Taxa Inforamation from Comparison Phentype</b>'),
            HTML("
This is the panel for the relative proportion of taxa found among the <b><i>comparison</i></b> phenotype among the Phylum, Class, Order, Family, Genus, and Species Levels. 
<hr>
<b>Notes: </b> This only includes taxa with correlations, and BH-adjusted p-values passed the thresholds defined in comparePhenotypes() command.               
"),
easyClose = TRUE
          )
        )
      })
      
      observeEvent(input$MS_Pheno2, {
        showModal(
          modalDialog(
            title = HTML('<b>Peek of the Avaliable Taxa Inforamation from Reference Phentype</b>'),
            HTML("
This is the panel for the relative proportion of taxa found among the <b><i>reference/control</i></b> phenotype among the Phylum, Class, Order, Family, Genus, and Species Levels. 
<hr>
<b>Notes: </b> This only includes taxa with correlations, and BH-adjusted p-values passed the thresholds defined in comparePhenotypes() command. 
"),
easyClose = TRUE
          )
        )
      })
      
      observeEvent(input$MS_PhenoComparison, {
        showModal(
          modalDialog(
            title = HTML('<b>Detailed Comparison between the Two Phenotypes</b>'),
            HTML("
This is an interactive table that includes all the shared and unique taxa between the comparison and reference phenotypes. <hr>
You can click the black triangle to expand the interactive table. <br>
The 'Full Taxa Name' will only display when a specific 'Taxonomic Level' is selected. 
"),
easyClose = TRUE
          )
        )
      })
      
observeEvent(input$MS_ModModComparison, {
        showModal(
          modalDialog(
            title = HTML('<b>Module-Module Taxa Comparison between Phenotypes</b>'),
            HTML("
The interactive heatmap highlights the shared taxa between the two phenotypes, the number of colors highlights the shared taxa. 
The taxa included here only includes taxa that have a correlation greater or equal to the correlation satisfied the correlation and BH-adjusted p-value cut-off values specified in comparePhenotypes() command. 
<br>
If a taxon is not found in either of the phenotypes, it will be labeled as 'Not Found.' The plot can be saved in HTML from 'Panel 2: Download Panel', or you can use the 'Download plot as a png' button on the top right corner of the plot (a default function from plotly). 
              "),
easyClose = TRUE
          )
        )
      })
      
      observeEvent(input$MS_ModPreservation, {
        showModal(
          modalDialog(
            title = HTML('<b>Module Preservation Statistics</b>'),
            HTML(
"The module preservation statistics plot shows the <i>medianRank</i> Vs. <i>Z<sub>Summary</sub></i> with the reference specified in comparePhenotypes() command.  
<br> The size of the dot represents the number of taxa in the module of the reference. "

            ),
easyClose = TRUE
          )
        )
      })
      
      observeEvent(input$MS_ModMembership, {
        showModal(
          modalDialog(
            title = HTML('<b>Module Taxa Memebrship between the Two Phenotypes</b>'),
            HTML(
"
This is an interactive table showing the taxa in each of the modules from both the disease and reference modules. In order to activate the table, you will need to click the module from the 'module preservation statistics' plot on the left.  <br> 
If the module overlapped each other, you could zoom in on the plot to click on the module.
"

            ),
easyClose = TRUE
          )
        )
      })

      observeEvent(input$MS_TaxaSelector, {
        showModal(
          modalDialog(
            title = HTML('<b>Select the Taxa You Want to Visualize on the Network Plot</b>'),
            HTML(
              "
This is an interactive module where you can click the taxa on the left column to include the selected taxon for investigation on the network plot below. <hr>
Multiple selections of taxa are allowed. <br>
If you want to remove a selected taxon, click its name on the right column. <br>
You can also search for the taxon you want by inputting the taxon in the search box. 
"

            ),
easyClose = TRUE
          )
        )
      })      
      
      observeEvent(input$MS_ModBarPlot, {
        showModal(
          modalDialog(
            title = HTML('<b>Module-based Taxa Inforamtion Bar Plot</b>'),
            HTML(
              "
<b>Note: </b> Please click on the filter button to activate the plot. 
<hr>
The box plot highlights the taxa membership per module from both phenotypes as well as its full taxonomic information, including all its parent taxonomic levels. <br>
By default, it displays all taxa, and you can also select a single taxon (selected from the Taxa Selector panel on the left). In this mode, all its children's taxonomic taxa will be shown, and all other taxa will be color in gray.  
<br>
You switch between the phenotypes and between the 'All' and individual taxa. <br>
If no selected taxa was in the dropdown menu, please random click another taxa on the left panel.
"

            ),
easyClose = TRUE
          )
        )
      })      
      
      observeEvent(input$MS_TaxaModTable, {
        showModal(
          modalDialog(
            title = HTML('<b>Number of Valid Taxa-Taxa Interactions for Selected Taxa</b>'),
            HTML(
              "
This table includes all the taxa-taxa interactions and their respecitve related module from the selected taxa. 
If no selected taxa was in the dropdown menu, please random click another taxa on the left panel.
"

            ),
easyClose = TRUE
          )
        )
      })      
      
      observeEvent(input$MS_Network, {
      showModal(modalDialog(
          title = "Interactive Network Plot Settings ", 
            tags$img(
              src = base64enc::dataURI(file = "www/NetworkLegend.png", mime = "image/png"),
              alt = "Italian Trulli",
              width = "200px"
            ),
          HTML("
The Network Plot section allows an interactive display of the selected taxa from the 'Taxa Selector' with a range of filtering settings accessible from the top left filter button. <br><b>Note:</b> <br>
<ul>
  <li>The node shape has priority shape from the top to the bottom of the legend order. E.g., if a taxon is significant from differential abundance analysis, it will be displayed as 'square' instead of 'star' even if it is a selected taxon. </li>
  <li>The border color represent the <b>Reference</b> module color, the internal color represent the <b>Comparison</b> module color as shown in the figure on the left. </li>
  <li> In the description below, we define a <i>significant connection</i> as a taxa-taxa connection that passed the correlation threshold (from both the slider input and the comparePhenotypes() function) and with a BH-adjusted p-value from comparePhenotypes(). </li>
</ul>
<hr>
<b>Node-Node Connections Mode:</b> <br>
<b>Only the selected taxa:</b> Only display the selected taxa. In this mode, only the selected taxa from the 'Taxa Selector' will render, and they will remain as nodes without connections if there are no significant connections. <br>
<b>All taxa directly related to selected taxa:</b> This is the <b><i>Suggested</i></b> mode for display. It will render all the taxa that shared significant connections with the selected taxa and intra-modular taxa that shared a significant connection with the selected taxa. <br>
<b>All taxa in related modules: </b> This is the mode to display all taxa that shared the same module membership from both the <b>Comparison</b> and <b>Reference</b> phenotypes. This mode will be likely to create multiple sub-graphs that might be harder to view. <hr>
<b>Connections from Share Common Phylogenetic Ancestor?</b> This is an optional method to remove/keep all significant connections that link between the taxa that share a common ancestor. E.g. The connection between the Family Bacteroidaceae and its children Genus <i>Bacteroides</i> will be  removed if the selection is 'Remove'. <hr>
<b>Edge Mode:</b>  The shard mode can filter the correlation based on their definition from the significant connections. <hr>
<b>Taxonomic Levels:</b> Filtering the taxa based on its taxonomic levels.<hr>
<b>Node Border Color Width, Node Label Size, Edge Label Size</b> Adjust the display setting for Nodes and Connections display settings. 


"),
          size = "l",
          
          easyClose = TRUE
        ))
      })
      
      observeEvent(input$MS_SingelTaxaViolin, {
        showModal(
          modalDialog(
            title = HTML('<b>Single Taxon Violin Plot</b>'),
            HTML(
              "This plot represents a single taxon and its respective log<sub>2</sub>-transformed count from both phenotypes. If one of them is missing, it means such taxon is missed from the respective phenotype. "            
            ),
            easyClose = TRUE
          )
        )
      })
      
      observeEvent(input$MS_TwoTaxaViolin, {
        showModal(
          modalDialog(
            title = HTML('<b>Two Taxa Violin Plot</b>'),
            HTML(
              "
Similar to the single taxon display above, this plot displays the log<sub>2</sub>-transformed count from both phenotypes for the sequentially clicked taxa. "            
            ),
            easyClose = TRUE
          )
        )
      })
      
            
      observeEvent(input$MS_DownloadPanel, {
        showModal(
          modalDialog(
            title = HTML('<b>Download Displayed Figures in Panel 1</b>'),
            HTML(
              "The download panel enables you to download the currently displayed plots from Panel 1 in PDF or PNG format. For the PNG format, the DPI is set to 300. The file should be saved to your default download folder with the plot type name followed by the date and time to avoid overwriting any plot. "            
            ),
            easyClose = TRUE
          )
        )
      })
      
      # stop App on closing the browser
      session$onSessionEnded(function() {
        stopApp()
      })
    },
    options = list(launch.browser = TRUE)
  )
}
zhouLabNCSU/C3NA documentation built on Oct. 10, 2022, 4:43 a.m.