R/app_server.R

Defines functions app_server

#' The application server-side
#'
#' @param input,output,session Internal parameters for {shiny}.
#'     DO NOT REMOVE.
#' @import shiny
#' @importFrom rlang .data
#' @import data.table
#' @import statmod
#'
#' @noRd

app_server <- function(input, output, session) {
  # PERFORMANCE SETTINGS
  n_cores <- getShinyOption("n_cores")
  options(shiny.maxRequestSize = getShinyOption("shiny.maxRequestSize"))

  # INITIALIZE REACTIVE VARIABLES
  rval_generated_limma_model <- reactiveVal(value = FALSE)
  rval_analysis_finished <- reactiveVal(value = FALSE)
  rval_filteredlist2heatmap_valid <- reactiveVal(value = FALSE)
  rval_filteredmcsea2heatmap_valid <- reactiveVal(value = FALSE)
  rval_dmrs_finished <- reactiveVal(value = FALSE)
  rval_dmrs_ready2heatmap <- reactiveVal(value = FALSE)
  rval_dmrs_ready2mcsea <- reactiveVal(value = FALSE)

  # Load button only shows if file is uploaded
  output$ui_button_input_load <- renderUI({
    if (!is.null(input$fileinput_input$datapath)) {
      return(actionButton("button_input_load", "Load Data"))
    } else {
      return()
    }
  })

  # Enable load button every time file input is updated
  observeEvent(input$fileinput_input, shinyjs::enable("button_input_load"))

  # When you press button_input_load, the data is unzipped and the metharray sheet is loaded
  rval_sheet <- eventReactive(input$button_input_load, {

    # Check if updated file is .zip
    validate(need(tools::file_ext(input$fileinput_input$datapath) == "zip", "File extension should be .zip"))

    shinyjs::disable("button_input_load") # disable the load button to avoid multiple clicks

    if (dir.exists(paste0(tempdir(), "/experiment_data"))) {
      unlink(paste0(tempdir(), "/experiment_data"), recursive = TRUE) # remove current files in target directory
    }

    zip::unzip(input$fileinput_input$datapath,
      exdir = paste0(tempdir(), "/experiment_data")
    ) # extracting zip

    sheet <- minfi::read.metharray.sheet(paste0(tempdir(), "/experiment_data"))

    # We check if sheet is correct
    # This is to prevent app crashes when zip updated is not correct.
    validate(
      need(
        is.data.frame(sheet) &
          any(colnames(sheet) %in% "Slide") &
          any(colnames(sheet) %in% "Array"),
        "SampleSheet is not correct. Please, check your samplesheet and your zip file."
      )
    )

    validate(
      need(
        anyDuplicated(colnames(sheet)) == 0,
        "Repeated variable names are not allowed. Please, modify your sample sheet."
      )
    )

    colnames(sheet) <- make.names(colnames(sheet)) # fix possible not-valid colnames

    sheet
  })


  rval_sheet_target <- eventReactive(
    input$button_input_next,
    rval_sheet()[rval_sheet()[[input$select_input_samplenamevar]] %in% input$selected_samples, ]
  )


  rval_clean_sheet_target <- eventReactive(rval_gset(), {
    generate_clean_samplesheet(target_samplesheet = minfi::pData(rval_gset()),
      donorvar = input$select_input_donorvar)

  })




  # When you press button_input_load, the form options are updated
  observeEvent(input$button_input_load, {
    updateSelectInput(
      session,
      "select_input_samplenamevar",
      label = "Select Sample Names Column:",
      choices = colnames(rval_sheet())
    )

    updateSelectInput(
      session,
      "select_input_groupingvar",
      label = "Select Variable of Interest:",
      choices = colnames(rval_sheet())
    )
    updateSelectInput(
      session,
      "select_input_donorvar",
      label = "Select Donor Variable:",
      choices = colnames(rval_sheet())
    )

    shinyjs::enable("button_input_next") # Enable button continue
  })


  # The checkbox of samples to process is updated when samplenamevar changes
  observeEvent(
    {
      input$select_input_samplenamevar
      input$select_input_groupingvar
    },
    updatePickerInput(
      session,
      "selected_samples",
      label = "Select Samples to Process:",
      selected = rval_sheet()[, input$select_input_samplenamevar],
      choices = rval_sheet()[, input$select_input_samplenamevar],
      choicesOpt = list(subtext = paste("Group: ", rval_sheet()[, input$select_input_groupingvar]))
    )
  )

  # when samples selected are changed, continue button is enabled again
  observeEvent(input$selected_samples, shinyjs::enable("button_input_next"))

  # The dataframe is rendered
  output$samples_table <- DT::renderDT(
    rval_sheet(),
    rownames = FALSE,
    selection = "single",
    style = "bootstrap",
    options = list(
      pageLength = 10,
      autoWidth = TRUE,
      scrollX = TRUE,
      columnDefs = list(list(
        targets = match("Basename", colnames(rval_sheet())) - 1, visible = FALSE
      ))
    )
  )




  # rval_rgset loads RGSet using read.metharray.exp and the sample sheet (rval_sheet())
  rval_rgset <- eventReactive(input$button_input_next, ignoreNULL = FALSE, {
    validate(need(input$fileinput_input != "", "Data has not been uploaded yet"))

    # Prior check to test variable selection
    if (anyDuplicated(rval_sheet_target()[, input$select_input_samplenamevar]) > 0 |
      anyDuplicated(rval_sheet_target()[, input$select_input_groupingvar]) == 0) {
      showModal(
        modalDialog(
          title = "Variable error",
          "Check if selected variables are correct. Sample Name Variable should not have duplicated values
          and the variable of interest should have groups greater than 1.",
          easyClose = TRUE,
          footer = NULL
        )
      )
    }

    # Check prior conditions to read data
    validate(need(
      anyDuplicated(rval_sheet_target()[, input$select_input_samplenamevar]) == 0,
      "Sample Name Variable should not have duplicated values"
    ))
    validate(need(
      anyDuplicated(rval_sheet_target()[, input$select_input_groupingvar]) > 0,
      "Grouping variable should have groups greater than 1"
    ))

    # disable button to avoid multiple clicks
    shinyjs::disable("button_input_next")


    # We need to check if this step works
    withProgress(
      message = "Reading array data...",
      value = 2,
      max = 5,
      {
        try({
          RGSet <- read_idats(
            targets = rval_sheet_target(),
            custom_anno = input$switch_input_customanno, 
            array = gsub(input$select_input_customarray, pattern = "\\|.*", replacement= ""), 
            annotation = gsub(input$select_input_customarray, pattern = ".*\\|", replacement= ""))
        })

        if (!exists("RGSet", inherits = FALSE)) {
          showModal(
            modalDialog(
              title = "reading error",
              "Minfi can't read arrays specified in your samplesheet. Please, check your zipfile and your sampleSheet",
              easyClose = TRUE,
              footer = NULL
            )
          )
          shinyjs::disable("button_minfi_select")
        }

        validate(
          need(
            exists("RGSet", inherits = FALSE),
            "Minfi can't read arrays specified in your samplesheet. Please, check your zipfile and your sampleSheet"
          )
        )
        
        #Checking array type and annotation
        nProbes = length(minfi::featureNames(RGSet))
        
        if(nProbes >= 622000 & nProbes <= 623000){
          
          if (!requireNamespace("IlluminaHumanMethylation450kanno.ilmn12.hg19", quietly = TRUE) |
              !requireNamespace("IlluminaHumanMethylation450kmanifest", quietly = TRUE))
          {
            showModal(
              modalDialog(
                title = "Missing package(s)",
                "450k annotation or manifest packages are not available. Please, install IlluminaHumanMethylation450kmanifest and IlluminaHumanMethylation450kanno.ilmn12.hg19 packages and restart the application.",
                easyClose = TRUE,
                footer = NULL
              )
            )
          }
          
          validate(
            need(
              requireNamespace("IlluminaHumanMethylation450kanno.ilmn12.hg19", quietly = TRUE) &
                requireNamespace("IlluminaHumanMethylation450kmanifest", quietly = TRUE),
              "450k annotation or manifest packages are not available. Please, install IlluminaHumanMethylation450kmanifest and IlluminaHumanMethylation450kanno.ilmn12.hg19 packages."
            )
          )
        }
        else if (nProbes >= 1032000 & nProbes <= 1053000){
          
          if (!requireNamespace("IlluminaHumanMethylationEPICanno.ilm10b4.hg19", quietly = TRUE) |
              !requireNamespace("IlluminaHumanMethylationEPICmanifest", quietly = TRUE))
          {
            showModal(
              modalDialog(
                title = "Missing package(s)",
                "EPIC annotation or manifest packages are not available. Please, install IlluminaHumanMethylationEPICmanifest and IlluminaHumanMethylationEPICanno.ilm10b4.hg19 packages and restart the application.",
                easyClose = TRUE,
                footer = NULL
              )
            )
          }

          validate(
            need(
              requireNamespace("IlluminaHumanMethylationEPICanno.ilm10b4.hg19", quietly = TRUE) &
                requireNamespace("IlluminaHumanMethylationEPICmanifest", quietly = TRUE),
              "EPIC annotation or manifest packages are not available. Please, install IlluminaHumanMethylationEPICmanifest and IlluminaHumanMethylationEPICanno.ilm10b4.hg19 packages."
            )
          )
        }

        # analysis restarted
        rval_analysis_finished(FALSE)
        rval_dmrs_finished(FALSE)
        
        # we return RGSet
        RGSet
      }
    )
  })

  # We change the page to the next one
  observeEvent(input$button_input_next, {
    # check if rgset is loaded
    req(rval_rgset())

    # update PCA parameters
    updateSelectInput(
      session,
      "select_minfi_pcaplot_pcx",
      choices = paste0("PC", seq_len(nrow(rval_sheet_target()))),
      selected = "PC1"
    )

    updateSelectInput(
      session,
      "select_minfi_pcaplot_pcy",
      choices = paste0("PC", seq_len(nrow(rval_sheet_target()))),
      selected = "PC2"
    )

    updateSelectInput(
      session,
      "select_minfi_pcaplot_color",
      choices = c(
        colnames(rval_sheet_target()),
        "xMed",
        "yMed",
        "predictedSex"
      ),
      selected = input$select_input_groupingvar
    )
    
    if(input$switch_input_customanno){
      updateSelectInput(
        session,
        "select_export_genometype",
        choices = "as is",
        selected = "as is"
      )
    }


    shinyjs::enable("button_minfi_select")
    updateNavbarPage(session, "navbar_epic", "Normalization")
  })


  # MINFI NORMALIZATION

  # Calculation of minfi normalized data
  rval_gset <- eventReactive(input$button_minfi_select, {
    validate(need(
      !is.null(rval_rgset()),
      "Raw data has not been loaded yet."
    ))

    shinyjs::disable("button_minfi_select") # disable button to avoid repeat clicking

    withProgress(
      message = "Normalization in progress...",
      value = 1,
      max = 4,
      {
        try({
          gset <- normalize_rgset(
            rgset = rval_rgset(), normalization_mode = input$select_minfi_norm,
            detectP = 0.01, dropSNPs = input$select_minfi_dropsnps, maf = input$slider_minfi_maf,
            dropCpHs = input$select_minfi_dropcphs, dropSex = input$select_minfi_chromosomes
          )
        })

        # check if normalization has worked

        if (!exists("gset", inherits = FALSE)) {
          showModal(
            modalDialog(
              title = "Normalization failure",
              "An unexpected error has occurred during minfi normalization. Please, notify the error to the package maintainer.",
              easyClose = TRUE,
              footer = NULL
            )
          )
          shinyjs::enable("button_minfi_select")
        }

        validate(
          need(
            exists("gset", inherits = FALSE),
            "An unexpected error has occurred during minfi normalization. Please, notify the error to the package maintainer."
          )
        )

        # enable button
        shinyjs::enable("button_minfi_select")

        # return gset
        gset
      }
    )
  })


  # filtered probes info

  rval_gsetprobes <- eventReactive(input$button_minfi_select, {
    req(rval_gset())
    length(minfi::featureNames(rval_gset()))
  })

  output$text_minfi_probes <- renderText(paste(rval_gsetprobes(), "positions after normalization"))

  # getBeta/getM reactives
  rval_rgset_getBeta <- eventReactive(rval_rgset(), {
    bvalues <- as.data.frame(minfi::getBeta(rval_rgset()))
    colnames(bvalues) <- rval_sheet_target()[[input$select_input_samplenamevar]]
    bvalues
  })

  rval_gset_getBeta <- eventReactive(rval_gset(), {
    bvalues <- as.data.frame(minfi::getBeta(rval_gset()))
    colnames(bvalues) <- rval_sheet_target()[[input$select_input_samplenamevar]]
    bvalues
  })

  rval_gset_getM <- reactive({
    mvalues <- minfi::getM(rval_gset())
    colnames(mvalues) <- rval_sheet_target()[[input$select_input_samplenamevar]]
    mvalues
  })
  ##############

  # Minfi Graphics

  # Density plots
  rval_plot_densityplotraw <- reactive(create_densityplot(rval_rgset_getBeta(), 200000))
  rval_plot_densityplot <- reactive(create_densityplot(rval_gset_getBeta(), 200000))

  output$graph_minfi_densityplotraw <- plotly::renderPlotly(rval_plot_densityplotraw())
  output$graph_minfi_densityplot <- plotly::renderPlotly(rval_plot_densityplot())

  # PCA
  rval_plot_pca <- eventReactive(
    list(input$button_pca_update, input$button_minfi_select),
    create_pca(
      Bvalues = rval_gset_getBeta(),
      pheno_info = as.data.frame(minfi::pData(rval_gset())),
      group = input$select_input_samplenamevar,
      pc_x = input$select_minfi_pcaplot_pcx,
      pc_y = input$select_minfi_pcaplot_pcy,
      color = input$select_minfi_pcaplot_color
    )
  )

  output$graph_minfi_pcaplot <- plotly::renderPlotly(rval_plot_pca()[["graph"]])

  output$table_minfi_pcaplot <- DT::renderDT(
    format(rval_plot_pca()[["info"]], digits = 2),
    rownames = TRUE,
    selection = "single",
    style = "bootstrap",
    options = list(
      autoWidth = TRUE,
      paging = FALSE,
      scrollX = TRUE,
      lengthChange = FALSE,
      searching = FALSE,
      info = FALSE
    )
  )

  # Correlations

  rval_plot_corrplot <- reactive(
    create_corrplot(
      rval_gset_getBeta(),
      rval_clean_sheet_target(),
      rval_sheet_target(),
      p.value = input$select_minfi_typecorrplot == "p.value"
    )
  )

  output$graph_minfi_corrplot <- plotly::renderPlotly(rval_plot_corrplot()[["graph"]])
  output$table_minfi_corrplot <- DT::renderDT(rval_plot_corrplot()[["info"]],
    rownames = FALSE,
    selection = "single",
    style = "bootstrap",
    caption = "Autodetected variable types:",
    options = list(pageLength = 10, autoWidth = TRUE)
  )

  # Boxplots

  rval_plot_boxplotraw <- reactive(create_boxplot(rval_rgset_getBeta()))
  rval_plot_boxplot <- reactive(create_boxplot(rval_gset_getBeta()))

  output$graph_minfi_boxplotraw <- renderPlot(rval_plot_boxplotraw())
  output$graph_minfi_boxplot <- renderPlot(rval_plot_boxplot())


  # QC plots

  rval_plot_qcraw <- reactive(create_plotqc(rval_rgset(), rval_sheet_target()[, input$select_input_samplenamevar]))
  rval_plot_bisulfiterawII <- reactive(create_bisulfiteplot(rval_rgset(), rval_sheet_target()[, input$select_input_samplenamevar]))

  output$graph_minfi_qcraw <- plotly::renderPlotly(rval_plot_qcraw())
  output$graph_minfi_bisulfiterawII <- plotly::renderPlotly(rval_plot_bisulfiterawII())

  # Sex prediction

  rval_plot_sexprediction <- reactive({
    req(rval_gset())
    create_sexplot(rval_gset(), rval_sheet_target()[, input$select_input_samplenamevar])
  })

  rval_plot_sextable <- reactive({
    req(rval_gset())
    data.frame(name = rval_sheet_target()[[input$select_input_samplenamevar]], sex = as.data.frame(minfi::pData(rval_gset()))[["predictedSex"]])
  })

  output$graph_minfi_sex <- plotly::renderPlotly(rval_plot_sexprediction())

  output$table_minfi_sex <- DT::renderDT(
    rval_plot_sextable(),
    rownames = FALSE,
    selection = "single",
    style = "bootstrap",
    caption = "Predicted sex:",
    options = list(
      pageLength = 10,
      scrollX = TRUE,
      autoWidth = TRUE
    )
  )


  # SNPs heatmap

  rval_plot_snpheatmap <- reactive(
    create_snpheatmap(
      minfi::getSnpBeta(rval_rgset()),
      rval_sheet_target()[, input$select_input_samplenamevar],
      rval_sheet_target()[, input$select_input_donorvar]
    )
  )

  output$graph_minfi_snps <- plotly::renderPlotly(rval_plot_snpheatmap())



  # Update of next form and move to Limma
  observeEvent(input$button_minfi_select, {
    # check if normalization has been performed
    req(rval_gset())

    updatePickerInput(
      session,
      "select_limma_voi",
      label = "Select Variable of Interest",
      choices = colnames(rval_clean_sheet_target())[vapply(rval_clean_sheet_target(), is.factor, logical(1))],
      selected = input$select_input_groupingvar
    )

    updatePickerInput(
      session,
      "checkbox_limma_covariables",
      label = "Select linear model covariables",
      choices = colnames(rval_clean_sheet_target()),
      selected = input$select_input_donorvar
    )

    # possible interactions of variables:
    if (length(colnames(rval_clean_sheet_target()) > 2)) {
      interactions <- utils::combn(colnames(rval_clean_sheet_target()), 2)
      interactions <- sprintf("%s:%s", interactions[1, ], interactions[2, ])
    }
    else {
      interactions <- c()
    }


    updatePickerInput(
      session,
      "checkbox_limma_interactions",
      label = "Select linear model interactions",
      choices = interactions,
      selected = input$select_input_donorvar
    )

    shinyjs::enable("button_limma_calculatemodel")
  })




  # LIMMA

  # Variable of interest
  rval_voi <- reactive(factor(make.names(minfi::pData(rval_gset())[, input$select_limma_voi])))

  # Design calculation
  rval_design <- eventReactive(input$button_limma_calculatemodel, {
    req(input$select_limma_voi) # a variable of interest is required

    # Bulding the design matrix
    try({
      design <- generate_design(
        voi = input$select_limma_voi, sample_name = input$select_input_samplenamevar,
        covariables = input$checkbox_limma_covariables, interactions = input$checkbox_limma_interactions,
        sample_sheet = rval_clean_sheet_target()
      )
    })

    validate(
      need(
        exists("design", inherits = FALSE),
        "Design matrix has failed. Please, check your options and try again."
      ),
      need(
        nrow(design) == length(rval_sheet_target()[[input$select_input_samplenamevar]]),
        "The design matrix contains missing values. Please, check the selected variable and covariables."
      )
    )

    # checking colinearity
    if (qr(design)$rank < ncol(design)) {
      showModal(
        modalDialog(
          title = "Colinearity warning",
          "The design matrix presents colinear columns. Even though it is possible to try to continue the analysis, checking the selected covariables is recommended.",
          easyClose = TRUE,
          footer = NULL
        )
      )
    }

    design
  })

  # Calculation of contrasts
  rval_contrasts <- reactive({
    generate_contrasts(rval_voi())
  })


  # Calculation of limma model
  rval_fit <- eventReactive(input$button_limma_calculatemodel, ignoreNULL = FALSE, {
    validate(
      need(input$fileinput_input != "", "DMP calculation has not been performed or data has not been uploaded.")
    )

    req(rval_design())

    shinyjs::disable("button_limma_calculatemodel") # disable button to avoid repeat clicking

    withProgress(
      message = "Generating linear model...",
      value = 3,
      max = 6,
      {
        try({
          fit <- generate_limma_fit(
            Mvalues = rval_gset_getM(), design = rval_design(),
            weighting = as.logical(input$select_limma_weights)
          )
        })

        if (!exists("fit", inherits = FALSE)) {
          rval_generated_limma_model(FALSE) # disable contrast button
          rval_analysis_finished(FALSE) # disable download buttons

          showModal(
            modalDialog(
              title = "lmFit error",
              "lmFit model has failed. Please, check your options and try again.",
              easyClose = TRUE,
              footer = NULL
            )
          )
        }
      }
    )

    shinyjs::enable("button_limma_calculatemodel") # enable button again to allow repeting calculation

    validate(need(
      exists("fit", inherits = FALSE),
      "lmFit model has failed. Please, check your options and try again."
    ))

    fit # returning linear model
  })


  # rval_fit() has NAs, we remove the option to trend or robust in eBayes to prevent failure
  observeEvent(input$button_limma_calculatemodel, {
    if (any(vapply(rval_fit(), function(x) {
      any(is.na(unlist(x)) |
        unlist(x) == Inf | unlist(x) == -Inf)
    }, logical(1)))) {
      message("NAs or Inf values detected, trend and robust options are disabled.")

      updateSwitchInput(session,
        "select_limma_trend",
        value = FALSE,
        disabled = TRUE
      )

      updateSwitchInput(session,
        "select_limma_robust",
        value = FALSE,
        disabled = TRUE
      )
    }

    else {
      message("NAs or Inf values not detected, trend and robust options are enabled")

      updateSwitchInput(session,
        "select_limma_trend",
        value = FALSE,
        disabled = FALSE
      )

      updateSwitchInput(session,
        "select_limma_robust",
        value = FALSE,
        disabled = FALSE
      )
    }

    # Creating calculate differences button
    rval_generated_limma_model(TRUE)
  })


  output$button_limma_calculatedifs_container <- renderUI({
    if (rval_generated_limma_model()) {
      return(tagList(
        br(),

        h4("Contrasts options"),

        switchInput(
          inputId = "select_limma_trend",
          label = "eBayes Trend",
          labelWidth = "80px",
          value = FALSE
        ),

        switchInput(
          inputId = "select_limma_robust",
          label = "eBayes Robust",
          labelWidth = "80px",
          value = FALSE
        ),

        actionButton("button_limma_calculatedifs", "Calc. Contrasts")
      ))
    } else {
      return()
    }
  })


  # render of plots and tables

  rval_plot_plotSA <- reactive(create_plotSA(rval_fit()))
  output$graph_limma_plotSA <- renderPlot(rval_plot_plotSA())
  output$table_limma_design <- DT::renderDT(
    rval_design(),
    rownames = TRUE,
    selection = "single",
    style = "bootstrap",
    options = list(
      autoWidth = TRUE,
      scrollX = TRUE,
      lengthChange = FALSE,
      searching = FALSE
    )
  )


  # Calculation of global difs
  rval_globaldifs <- eventReactive(input$button_limma_calculatedifs, {
    try({
      globaldifs <- calculate_global_difs(rval_gset_getBeta(), rval_voi(), rval_contrasts(),
        cores =
          n_cores
      )
    })

    if (!exists("globaldifs", inherits = FALSE)) {
      showModal(
        modalDialog(
          title = "Global difs calculation error",
          "An unexpected error has ocurred during global diffs and means calculation. Please, check your selected samples.",
          easyClose = TRUE,
          footer = NULL
        )
      )
    }

    validate(
      need(
        exists("globaldifs", inherits = FALSE),
        "An unexpected error has ocurred during global diffs and means calculation."
      )
    )

    globaldifs
  })

  # Calculation of differences (eBayes)
  rval_finddifcpgs <- eventReactive(input$button_limma_calculatedifs, {
    try({
      dif_cpgs <- find_dif_cpgs(
        design = rval_design(),
        fit = rval_fit(),
        contrasts = rval_contrasts(),
        trend = as.logical(input$select_limma_trend),
        robust = as.logical(input$select_limma_robust),
        cores = n_cores
      )
    })

    if (!exists("dif_cpgs", inherits = FALSE)) {
      showModal(
        modalDialog(
          title = "Contrasts Calculation Error",
          "An unexpected error has ocurred during contrasts calculation. Please, generate the model again and check if it is correct.
        If the problem persists, report the error to the maintainer",
          easyClose = TRUE,
          footer = NULL
        )
      )

      rval_generated_limma_model(FALSE)
      rval_analysis_finished(FALSE)
      shinyjs::disable("button_limma_heatmapcalc")
    }

    validate(
      need(
        exists("dif_cpgs", inherits = FALSE),
        "An unexpected error has ocurred during contrasts calculation. Please, generate the model again and check if it is correct.
        If the problem persists, report the error to the maintainer"
      )
    )

    rval_analysis_finished(TRUE)

    dif_cpgs
  })

  # Update of heatmap controls
  observeEvent(input$button_limma_calculatedifs, {
    updateTabsetPanel(session, "tabset_limma", "differential_cpgs")

    updateSelectInput(
      session,
      "select_limma_groups2plot",
      label = "Groups to plot",
      choices = levels(rval_voi()),
      selected = levels(rval_voi())
    )

    updateSelectInput(
      session,
      "select_limma_contrasts2plot",
      label = "Contrasts to plot",
      choices = rval_contrasts(),
      selected = rval_contrasts()
    )

    updateSelectInput(
      session,
      "select_limma_anncontrast",
      label = "Contrast",
      choices = rval_contrasts()
    )

    # disable button to avoid repeat clicking
    shinyjs::disable("button_limma_calculatedifs")

    # force rval_filteredlist
    rval_filteredlist()

    # enable or disable removebatch option
    covariables_design <- as.matrix(rval_design()[, -seq_len(length(unique(rval_voi())))])

    if (ncol(covariables_design) > 0) {
      updateSwitchInput(session,
        "select_limma_removebatch",
        value = FALSE,
        disabled = FALSE
      )
      updateSwitchInput(session,
        "select_dmrs_removebatch",
        value = FALSE,
        disabled = FALSE
      )
    }
    else {
      updateSwitchInput(session,
        "select_limma_removebatch",
        value = FALSE,
        disabled = TRUE
      )
      updateSwitchInput(session,
        "select_dmrs_removebatch",
        value = FALSE,
        disabled = FALSE
      )
    }

    # enable and click heatmap button to get default graph
    shinyjs::enable("button_limma_heatmapcalc")
    shinyjs::click("button_limma_heatmapcalc")

    # enable again the button to allow repeat calculation
    shinyjs::enable("button_limma_calculatedifs")
  })

  # Calculation of filtered list
  rval_filteredlist <- reactive({
    withProgress(
      message = "Performing contrasts calculations...",
      value = 1,
      max = 6,
      {
        setProgress(message = "Calculating global difs...", value = 1)
        rval_globaldifs()
        setProgress(message = "Calculating eBayes...", value = 4)
        rval_finddifcpgs()
        setProgress(message = "Calculating filtered list...", value = 5)
        create_filtered_list(
          rval_finddifcpgs(),
          rval_globaldifs(),
          deltaB = input$slider_limma_deltab,
          adjp_max = input$slider_limma_adjpvalue,
          p.value = input$slider_limma_pvalue,
          cores = n_cores
        )
      }
    )
  })

  rval_filteredlist2heatmap <- reactive({
    join_table <- create_dmps_heatdata(
      rval_filteredlist(),
      input$select_limma_contrasts2plot,
      input$select_limma_removebatch,
      rval_design(),
      rval_voi(),
      rval_gset_getBeta()
    )

    # If the number of CpGs is not in the plotting range, return NULL to avoid errors in plot_heatmap and disable download
    if (is.null(join_table) |
      nrow(join_table) < 2 | nrow(join_table) > 12000) {
      rval_filteredlist2heatmap_valid(FALSE)
      NULL
    }
    else {
      rval_filteredlist2heatmap_valid(TRUE)
      join_table
    }
  })

  rval_cpgcount_heatmap <- eventReactive(input$button_limma_heatmapcalc, nrow(rval_filteredlist2heatmap()))

  rval_dendrogram <- eventReactive(input$button_limma_heatmapcalc, {
    if (input$select_limma_rowsidecolors) {

      # check if dendrogram cutting works (k should be minor than heatmap rows)
      try({
        dendrogram <- create_dendrogram(
          rval_filteredlist2heatmap(),
          factorgroups = factor(rval_voi()[rval_voi() %in% input$select_limma_groups2plot],
            levels = input$select_limma_groups2plot
          ),
          groups2plot = rval_voi() %in% input$select_limma_groups2plot,
          clusteralg = input$select_limma_clusteralg,
          distance = input$select_limma_clusterdist,
          scale_selection = input$select_limma_scale,
          k_number = input$select_limma_knumber
        )
      })
    } else {
      dendrogram <- NULL
    }


    if (!exists("dendrogram", inherits = FALSE)) {
      dendrogram <- NULL
      showModal(
        modalDialog(
          title = "Row clustering error",
          "An error has ocurred during cluster cutting from row dendrogram. Maybe the number of clusters selected is too high.",
          easyClose = TRUE,
          footer = NULL
        )
      )
    }

    dendrogram # returning the dendrogram classification
  })

  plot_heatmap <- eventReactive(input$button_limma_heatmapcalc, {
    validate(
      need(
        !is.null(rval_filteredlist2heatmap()),
        "Differences are not in the plotting range (<12000, >1)"
      ),
      need(
        !is.null(input$select_limma_groups2plot) &
          input$select_limma_groups2plot != "",
        "Select at least one group to plot."
      )
    )

    create_heatmap(
      rval_filteredlist2heatmap(),
      factorgroups = factor(rval_voi()[rval_voi() %in% input$select_limma_groups2plot],
        levels = input$select_limma_groups2plot
      ),
      groups2plot = rval_voi() %in% input$select_limma_groups2plot,
      Colv = as.logical(input$select_limma_colv),
      ColSideColors = input$select_limma_colsidecolors,
      RowSideColors = rval_dendrogram(),
      clusteralg = input$select_limma_clusteralg,
      distance = input$select_limma_clusterdist,
      scale = input$select_limma_scale,
      static = as.logical(input$select_limma_graphstatic)
    )
  })

  make_table <- eventReactive(input$button_limma_heatmapcalc, {
    default_df <- data.frame(
      contrast = rval_contrasts(),
      Hypermethylated = 0,
      Hypomethylated = 0,
      total = 0
    )

    count_df <- data.table::rbindlist(rval_filteredlist(), idcol = "contrast")

    if (nrow(count_df) > 0 & !is.null(count_df)) {
      count_df <- count_df %>%
        dplyr::mutate(type = factor(
          ifelse(
            .data$dif_current < 0,
            "Hypermethylated",
            "Hypomethylated"
          ),
          levels = c("Hypermethylated", "Hypomethylated")
        )) %>%
        dplyr::group_by(.data$contrast, .data$type) %>%
        dplyr::summarise(CpGs = dplyr::n()) %>%
        dplyr::ungroup() %>%
        tidyr::complete(.data$contrast, .data$type, fill = list(
          CpGs = 0)
        ) %>%
        tidyr::pivot_wider(
          names_from = .data$type,
          values_from = .data$CpGs
        ) %>%
        dplyr::mutate(total = .data$Hypermethylated + .data$Hypomethylated) %>%
        dplyr::mutate(dplyr::across(
          c("Hypermethylated", "Hypomethylated", "total"),
          ~ format(., scientific = FALSE)
        ))
    }

    rbind(as.data.frame(count_df), default_df[!(default_df[["contrast"]] %in% count_df[["contrast"]]), ])
  })



  observe({
    # Render the correct plot depending on the selected
    output$graph_limma_heatmapcontainer <- renderUI({
      if (!as.logical(input$select_limma_graphstatic)) {
        return(
          plotly::plotlyOutput(
            "graph_limma_heatmap_interactive",
            width = "600px",
            height = "500px"
          ) %>% shinycssloaders::withSpinner()
        )
      } else {
        return(
          div(
            min_width = 400,
            plotOutput(
              "graph_limma_heatmap_static",
              width = "600px",
              height = "600px"
            ) %>% shinycssloaders::withSpinner()
          )
        )
      }
    })

    if (!as.logical(input$select_limma_graphstatic)) {
      output$graph_limma_heatmap_interactive <- plotly::renderPlotly(plot_heatmap())
    } else {
      output$graph_limma_heatmap_static <- renderPlot(plot_heatmap())
    }
  })

  output$text_limma_heatmapcount <- renderText(paste("DMPs in heatmap:", rval_cpgcount_heatmap()))

  output$table_limma_difcpgs <- renderTable(make_table(), digits = 0)

  table_annotation <- eventReactive(list(input$button_limma_heatmapcalc, input$select_limma_anncontrast), {
    req(rval_filteredlist())

    dif_target <- paste("dif",
      limma::strsplit2(input$select_limma_anncontrast, "-")[1],
      limma::strsplit2(input$select_limma_anncontrast, "-")[2],
      sep = "_"
    )

    temp <- rval_annotation()[row.names(rval_annotation()) %in% rval_filteredlist()[[input$select_limma_anncontrast]]$cpg, ]
    temp$dif_beta <- rval_globaldifs()[[dif_target]][rval_globaldifs()[["cpg"]] %in% row.names(temp)]
    temp$fdr <- rval_filteredlist()[[input$select_limma_anncontrast]][["adj.P.Val"]][rval_filteredlist()[[input$select_limma_anncontrast]][["cpg"]] %in% row.names(temp)]

    temp
  })

  output$table_limma_ann <- DT::renderDT(
    table_annotation(),
    extensions = "Buttons",
    rownames = FALSE,
    selection = "single",
    style = "bootstrap",
    caption = "DMPs Annotation:",
    options = list(
      dom = "Blfrtip",
      lengthMenu = list(c(10, 25, 50, 100, 25000), c(10, 25, 50, 100, 25000)),
      pageLength = 10,
      autoWidth = TRUE,
      scrollX = TRUE,
      buttons = c("csv", "excel", "print")
    ),
  )

  ind_boxplot <- eventReactive(input$button_limma_indboxplotcalc, {
    validate(need(!is.null(input$table_limma_ann_rows_selected), "A DMP should be selected."))
    cpg_sel <- table_annotation()[["Name"]][input$table_limma_ann_rows_selected]

    create_individual_boxplot(rval_gset_getBeta(), cpg_sel, rval_voi())
  })

  output$graph_limma_indboxplot <- renderPlot(ind_boxplot())


  # Disable or enable buttons depending on software state
  observeEvent(
    rval_analysis_finished(),
    if (rval_analysis_finished()) {
      shinyjs::enable("download_export_robjects")
      shinyjs::enable("download_export_filteredbeds")
      shinyjs::enable("download_export_markdown")
      shinyjs::enable("download_export_script")
      shinyjs::enable("button_dmrs_calculate")

      updatePickerInput(
        session,
        "select_dmrs_contrasts",
        selected = rval_contrasts(),
        choices = rval_contrasts(),
      )
      updatePickerInput(
        session,
        "select_dmrs_platform",
        selected = if (nrow(rval_finddifcpgs()[[1]]) > 500000) {
          "EPIC"
        } else {
          "450k"
        },
        choices = c("450k", "EPIC")
      )
    }

    else {
      shinyjs::disable("download_export_robjects")
      shinyjs::disable("download_export_filteredbeds")
      shinyjs::disable("download_export_markdown")
      shinyjs::disable("download_export_script")
      shinyjs::disable("download_export_heatmaps")
      shinyjs::disable("button_dmrs_calculate")
    }
  )


  # DMRs

  rval_mcsea <- eventReactive(input$button_dmrs_calculate, {
    validate(
      need(
        rval_analysis_finished(),
        "To calculate DMRs, you have to finish first the DMP calculation."
      )
    )

    validate(
      need(
        requireNamespace("mCSEA", quietly = TRUE),
        "mCSEA is not installed. You should install the package to calculate DMRs"
      ),
      need(
        input$select_dmrs_contrasts != "",
        "You should select at least one contrast."
      ),
      need(
        input$select_dmrs_regions != "",
        "You should select at least one DMR type."
      )
    )

    updateSelectInput(session,
      "select_dmrs_selcont",
      choices = input$select_dmrs_contrasts
    )
    updateSelectInput(session,
      "select_dmrs_selreg",
      choices = input$select_dmrs_regions
    )

    updateSelectInput(
      session,
      "select_dmrs_groups2plot",
      label = "Groups to plot",
      choices = levels(rval_voi()),
      selected = levels(rval_voi())
    )
    updateSelectInput(
      session,
      "select_dmrs_contrasts2plot",
      label = "Contrasts to plot",
      choices = input$select_dmrs_contrasts,
      selected = input$select_dmrs_contrasts
    )
    updateSelectInput(
      session,
      "select_dmrs_regions2plot",
      label = "Regions to plot",
      choices = input$select_dmrs_regions,
      selected = input$select_dmrs_regions
    )

    shinyjs::disable("button_dmrs_calculate")

    withProgress(
      message = "Calculating DMRs...",
      max = 3,
      value = 1,
      {
        try({
          dmrs_result <- find_dmrs(
            rval_finddifcpgs(),
            minCpGs = input$slider_dmrs_cpgs,
            platform = "EPIC",
            voi = rval_voi(),
            regionsTypes = input$select_dmrs_regions,
            contrasts = input$select_dmrs_contrasts,
            bvalues = rval_gset_getBeta(),
            permutations = input$slider_dmrs_permutations,
            ncores = n_cores
          )

          setProgress(value = 2, message = "Calculating differential of betas...")

          dmrs_result <- add_dmrs_globaldifs(
            mcsea_result = dmrs_result,
            cpg_globaldifs = rval_globaldifs(),
            regionsTypes = input$select_dmrs_regions
          )
        })
      }
    )

    shinyjs::enable("button_dmrs_calculate")

    if (!exists("dmrs_result", inherits = FALSE)) {
      rval_dmrs_finished(FALSE)
      showModal(
        modalDialog(
          title = "DMR calculation error",
          "An unexpected error has occurred during DMRs calculation.",
          easyClose = TRUE,
          footer = NULL
        )
      )
      shinyjs::disable("button_dmrs_heatmapcalc")
    }

    validate(need(
      exists("dmrs_result", inherits = FALSE),
      "An unexpected error has occurred during DMRs calculation."
    ))

    # enable heatmap button
    shinyjs::enable("button_dmrs_heatmapcalc")
    rval_dmrs_finished(TRUE)
    rval_dmrs_ready2heatmap(TRUE)

    dmrs_result
  })

  rval_filteredmcsea <- eventReactive(list(input$button_dmrs_calculate, input$button_dmrs_heatmapcalc), {
    req(rval_mcsea())

    filter_dmrs(
      mcsea_list = rval_mcsea(),
      fdr = input$slider_dmrs_adjpvalue,
      pval = input$slider_dmrs_pvalue,
      dif_beta = input$slider_dmrs_deltab,
      regionsTypes = input$select_dmrs_regions,
      contrasts = input$select_dmrs_contrasts
    )
  })

  rval_filteredmcsea2heatmap <- reactive({
    req(input$select_dmrs_regions2plot)
    req(input$select_dmrs_contrasts2plot)

    # filtering contrasts
    join_table <- create_dmrs_heatdata(
      mcsea_result = rval_filteredmcsea(),
      bvalues = rval_gset_getBeta(),
      regions = input$select_dmrs_regions2plot,
      contrasts = input$select_dmrs_contrasts2plot,
      removebatch = input$select_dmrs_removebatch,
      design = rval_design(),
      voi = rval_voi()
    )

    # If the number of CpGs is not in the plotting range, return NULL to avoid errors in plot_dmrsheatmap
    if (is.null(join_table) |
      nrow(join_table) < 2 | nrow(join_table) > 12000) {
      rval_filteredmcsea2heatmap_valid(FALSE)
      NULL
    }
    else {
      rval_filteredmcsea2heatmap_valid(TRUE)
      join_table
    }
  })

  observeEvent(rval_dmrs_ready2heatmap(), {
    if (rval_dmrs_ready2heatmap()) {
      rval_dmrs_ready2heatmap(FALSE)

      shinyjs::click("button_dmrs_heatmapcalc")
    }
  })

  # Heatmap DMRs

  plot_dmrsheatmap <- eventReactive(input$button_dmrs_heatmapcalc, ignoreNULL = FALSE, {
    validate(
      need(
        requireNamespace("mCSEA", quietly = TRUE),
        "mCSEA is not installed. You should install the package to calculate DMRs."
      )
    )

    validate(
      need(
        input$fileinput_input != "",
        "DMR calculation has not been performed or data has not been uploaded."
      ),
      need(
        !is.null(input$select_dmrs_groups2plot) &
          input$select_dmrs_groups2plot != "",
        "Select at least one group to plot."
      ),
      need(
        !is.null(rval_filteredmcsea2heatmap()),
        "Differences are not in the plotting range (<12000, >1)"
      )
    )

    create_heatmap(
      rval_filteredmcsea2heatmap(),
      factorgroups = factor(rval_voi()[rval_voi() %in% input$select_dmrs_groups2plot],
        levels = input$select_dmrs_groups2plot
      ),
      groups2plot = rval_voi() %in% input$select_dmrs_groups2plot,
      Colv = as.logical(input$select_dmrs_colv),
      ColSideColors = input$select_dmrs_colsidecolors,
      RowSideColors = rval_dendrogram_dmrs(),
      clusteralg = input$select_dmrs_clusteralg,
      distance = input$select_dmrs_clusterdist,
      scale = input$select_dmrs_scale,
      static = as.logical(input$select_dmrs_graphstatic)
    )
  })

  rval_dendrogram_dmrs <- eventReactive(input$button_dmrs_heatmapcalc, ignoreNULL = FALSE, {
    if (input$select_dmrs_rowsidecolors) {

      # check if dendrogram cutting works (k should be minor than heatmap rows)
      try({
        dendrogram <- create_dendrogram(
          rval_filteredmcsea2heatmap(),
          factorgroups = factor(rval_voi()[rval_voi() %in% input$select_dmrs_groups2plot],
            levels = input$select_dmrs_groups2plot
          ),
          groups2plot = rval_voi() %in% input$select_dmrs_groups2plot,
          clusteralg = input$select_dmrs_clusteralg,
          distance = input$select_dmrs_clusterdist,
          scale_selection = input$select_dmrs_scale,
          k_number = input$select_dmrs_knumber
        )
      })
    } else {
      dendrogram <- NULL
    }


    if (!exists("dendrogram", inherits = FALSE)) {
      dendrogram <- NULL
      showModal(
        modalDialog(
          title = "Row clustering error",
          "An error has ocurred during cluster cutting from row dendrogram. Maybe the number of clusters selected is too high.",
          easyClose = TRUE,
          footer = NULL
        )
      )
    }

    dendrogram # returning the dendrogram classification
  })
  rval_cpgcount_dmrs_heatmap <- eventReactive(input$button_dmrs_heatmapcalc, nrow(rval_filteredmcsea2heatmap()))

  observe({
    # Render the correct plot depending on the selected
    output$graph_dmrs_heatmapcontainer <- renderUI({
      if (!as.logical(input$select_dmrs_graphstatic)) {
        return(
          plotly::plotlyOutput(
            "graph_dmrs_heatmap_interactive",
            width = "600px",
            height = "500px"
          ) %>% shinycssloaders::withSpinner()
        )
      } else {
        return(
          div(
            min_width = 400,
            plotOutput(
              "graph_dmrs_heatmap_static",
              width = "600px",
              height = "600px"
            ) %>% shinycssloaders::withSpinner()
          )
        )
      }
    })

    if (!as.logical(input$select_dmrs_graphstatic)) {
      output$graph_dmrs_heatmap_interactive <- plotly::renderPlotly(plot_dmrsheatmap())
    } else {
      output$graph_dmrs_heatmap_static <- renderPlot(plot_dmrsheatmap())
    }
  })

  output$text_dmrs_heatmapcount <- renderText(paste("DMRs in heatmap:", rval_cpgcount_dmrs_heatmap()))


  # DMRs count table

  make_table_dmrscount <- eventReactive(rval_filteredmcsea(), {
    result <- data.frame(
      contrast = rep(
        names(rval_filteredmcsea()),
        each = length(input$select_dmrs_regions)
      ),
      region_type = input$select_dmrs_regions
    )


    result$Hypermethylated <- apply(result, 1, function(x) {
      nrow(rval_filteredmcsea()[[x[1]]][[x[2]]][rval_filteredmcsea()[[x[1]]][[x[2]]]$NES < 0, ])
    })

    result$Hypomethylated <- apply(result, 1, function(x) {
      nrow(rval_filteredmcsea()[[x[1]]][[x[2]]][rval_filteredmcsea()[[x[1]]][[x[2]]]$NES > 0, ])
    })

    result$total <- result$Hypermethylated + result$Hypomethylated

    result
  })

  output$table_dmrs_count <- renderTable(make_table_dmrscount(), digits = 0)

  # DMRs single plot


  plot_singledmr <- eventReactive(input$button_dmrs_graphsingle, {
    validate(need(!is.null(input$table_dmrs_table_rows_selected), "A DMR should be selected."))

    selected_dmr <- row.names(rval_filteredmcsea()[[input$select_dmrs_selcont]][[input$select_dmrs_selreg]])[input$table_dmrs_table_rows_selected]
    col <- grDevices::rainbow(length(levels(rval_voi())))
    
    mCSEA::mCSEAPlot(
      rval_mcsea()[[input$select_dmrs_selcont]],
      regionType = input$select_dmrs_selreg,
      dmrName = selected_dmr,
      makePDF = FALSE,
      transcriptAnnotation = "symbol",
      col = col
    )
  })



  output$graph_dmrs_singledmr <- renderPlot(plot_singledmr())


  rval_table_sigdmrs <- reactive({
    rval_filteredmcsea()[[input$select_dmrs_selcont]][[input$select_dmrs_selreg]]
  })

  output$table_dmrs_table <- DT::renderDT(
    rval_table_sigdmrs(),
    rownames = TRUE,
    extensions = "Buttons",
    selection = "single",
    style = "bootstrap",
    caption = "Select DMR to plot:",
    options = list(
      pageLength = 10,
      dom = "Blfrtip",
      lengthMenu = list(c(10, 25, 50, 100, 25000), c(10, 25, 50, 100, 25000)),
      autoWidth = TRUE,
      scrollX = TRUE,
      buttons = c("csv", "excel", "print"),
      columnDefs = list(
        list(
          targets = match("leadingEdge", colnames(rval_filteredmcsea()[[input$select_dmrs_selcont]][[input$select_dmrs_selreg]])),
          visible = FALSE
        )
      )
    )
  )




  # EXPORT

  # R Objects

  output$download_export_robjects <- downloadHandler(
    "R_Objects.zip",
    content = function(file) {
      shinyjs::disable("download_export_robjects")
      withProgress(
        message = "Generating R Objects...",
        value = 1,
        max = 4,
        {
          rgset <- rval_rgset()
          gset <- rval_gset()
          fit <- rval_fit()
          design <- rval_design()
          ebayes_tables <- rval_finddifcpgs()
          bvalues <- rval_gset_getBeta()
          mvalues <- rval_gset_getM()
          global_difs <- rval_globaldifs()

          if (rval_dmrs_finished()) {
            dmr_results <- rval_mcsea()
          }
          else {
            dmr_results <- NULL
          }

          objetos <- list(
            RGSet = "rgset",
            GenomicRatioSet = "gset",
            fit = "fit",
            design = "design",
            ebayestables = "ebayes_tables",
            Bvalues = "bvalues",
            Mvalues = "mvalues",
            global_difs = "global_difs",
            dmr_results = "dmr_results"
          )

          setProgress(value = 2, message = "Saving RObjects...")

          for (id in input$select_export_objects2download) {
            do.call(function(object, name) saveRDS(object, file = paste0(tempdir(), "/", name, ".RDS")), list(object = as.name(objetos[[id]]), name = objetos[[id]]))
          }

          objects2save <- list.files(tempdir(), pattern = "*.RDS", full.names = TRUE)

          setProgress(value = 3, message = "Compressing RObjects...")
          zip::zip(
            file,
            objects2save,
            recurse = FALSE,
            include_directories = FALSE,
            mode = "cherry-pick"
          )
          shinyjs::enable("download_export_robjects")
        }
      )
    }
  )

  rval_annotation <- reactive({
    int_cols <- c(
      "Name",
      "Relation_to_Island",
      "UCSC_RefGene_Name",
      "UCSC_RefGene_Group",
      "GencodeV41_Name",
      "chr",
      "pos",
      "strand",
      "EPICv1_Loci"
    )

    if (input$select_export_genometype == "hg38" &
      (!requireNamespace("rtracklayer", quietly = TRUE)) |
      !requireNamespace("GenomicRanges", quietly = TRUE)) {
      showModal(
        modalDialog(
          title = "rtracklayer::liftOver not available",
          "Rtracklayer is not installed and it is needed to liftOver annotation from hg19 to hg38 genome. Please, install the package and restart the R session.",
          easyClose = TRUE,
          footer = NULL
        )
      )
      updateSelectInput(session,
        "select_export_genometype",
        choices = "hg19",
        selected = "hg19"
      )
    }

    withProgress(
      message = "Generating annotation...",
      max = 3,
      value = 1,
      {
        annotation <- as.data.frame(minfi::getAnnotation(rval_gset()))
        annotation <- annotation[, int_cols[int_cols %in% colnames(annotation)]]
        annotation$genome <- input$select_export_genometype

        if (input$select_export_genometype == "hg19" | input$select_export_genometype == "as is") {
          annotation
        }
        else {
          chain <- rtracklayer::import.chain(system.file("hg19ToHg38.over.chain", package = "shinyepico"))

          ann_granges <- data.frame(
            chr = annotation$chr,
            start = annotation$pos - 1,
            end = annotation$pos,
            name = row.names(annotation)
          )
          ann_granges <- GenomicRanges::makeGRangesFromDataFrame(
            ann_granges,
            starts.in.df.are.0based = TRUE,
            keep.extra.columns = TRUE
          )
          ann_granges <- unlist(rtracklayer::liftOver(ann_granges, chain = chain))

          hg38 <- data.table::data.table(
            Name = GenomicRanges::mcols(ann_granges)[[1]],
            chr = as.character(GenomicRanges::seqnames(ann_granges)),
            pos = GenomicRanges::start(ann_granges),
            genome = "hg38"
          )

          annotation <- as.data.table(annotation[, !(colnames(annotation) %in% c("chr", "pos", "genome"))])

          hg38 <- as.data.frame(data.table::merge.data.table(
            x = annotation,
            y = hg38,
            by = "Name",
            all.x = TRUE
          ))
          row.names(hg38) <- hg38$Name

          hg38
        }
      }
    )
  })

  # Bed downloading enabling/disabling control
  observe({
    if (input$select_export_analysistype == "DMPs") {
      if (rval_analysis_finished() &
        rval_filteredlist2heatmap_valid() &
        !is.null(rval_dendrogram())) {
        shinyjs::enable("download_export_filteredbeds")
      } else if (rval_analysis_finished() &
        input$select_export_bedtype == "by contrasts") {
        shinyjs::enable("download_export_filteredbeds")
      }
      else {
        shinyjs::disable("download_export_filteredbeds")
      }
    }
    else if (input$select_export_analysistype == "DMRs") {
      if (rval_dmrs_finished() &
        rval_filteredmcsea2heatmap_valid() &
        !is.null(rval_dendrogram_dmrs())) {
        shinyjs::enable("download_export_filteredbeds")
      } else if (rval_dmrs_finished() &
        input$select_export_bedtype == "by contrasts") {
        shinyjs::enable("download_export_filteredbeds")
      } else {
        shinyjs::disable("download_export_filteredbeds")
      }
    }
  })

  # heatmap downloading enable/disable
  observe({
    if (input$select_export_heatmaptype == "DMPs") {
      if (rval_analysis_finished() & rval_filteredlist2heatmap_valid()) {
        shinyjs::enable("download_export_heatmaps")
      } else {
        shinyjs::disable("download_export_heatmaps")
      }
    }
    else {
      if (rval_dmrs_finished() & rval_filteredmcsea2heatmap_valid()) {
        shinyjs::enable("download_export_heatmaps")
      } else {
        shinyjs::disable("download_export_heatmaps")
      }
    }
  })


  # Filtered BEDs
  output$download_export_filteredbeds <- downloadHandler(
    "filtered_beds.zip",
    content = function(file) {
      shinyjs::disable("download_export_filteredbeds")
      withProgress(
        message = "Generating BED files...",
        value = 1,
        max = 4,
        {
          if (input$select_export_bedtype == "by heatmap cluster" & input$select_export_analysistype == "DMPs") {
            create_filtered_bed_clusters(
              dendro_data = rval_dendrogram(),
              annotation = rval_annotation(),
              directory = dirname(file)
            )
          }
          else if (input$select_export_bedtype == "by contrasts" & input$select_export_analysistype == "DMPs") {
            create_filtered_beds(rval_filteredlist(),
              rval_annotation(),
              directory = dirname(file)
            )
          }
          else if (input$select_export_bedtype == "by heatmap cluster" & input$select_export_analysistype == "DMRs") {
            create_filtered_bed_dmrs_clusters(
              dendro_data = rval_dendrogram_dmrs(),
              mcsea_filtered = rval_filteredmcsea(),
              annotation = rval_annotation(),
              directory = dirname(file)
            )
            create_dmrs_bed_background(
              mcsea_result = rval_mcsea(),
              collapse = TRUE,
              regionsTypes = input$select_dmrs_regions2plot,
              annotation = rval_annotation(),
              directory = dirname(file)
            )
          }
          else if (input$select_export_bedtype == "by contrasts" & input$select_export_analysistype == "DMRs") {
            create_filtered_beds_dmrs(
              mcsea_filtered = rval_filteredmcsea(),
              regionsTypes = input$select_dmrs_regions,
              annotation = rval_annotation(),
              directory = dirname(file)
            )
            create_dmrs_bed_background(
              mcsea_result = rval_mcsea(),
              collapse = FALSE,
              regionsTypes = input$select_dmrs_regions,
              annotation = rval_annotation(),
              directory = dirname(file)
            )
          }


          objects <- list.files(
            path = dirname(file),
            full.names = TRUE,
            pattern = "*.bed"
          )

          zip::zip(
            file,
            objects,
            recurse = FALSE,
            include_directories = FALSE,
            mode = "cherry-pick"
          )

          file.remove(objects) # removing objects after exporting

          shinyjs::enable("download_export_filteredbeds")
        }
      )
    }
  )


  # Markdown Report
  output$download_export_markdown <- downloadHandler(
    filename = "Report.html",
    content = function(file) {
      shinyjs::disable("download_export_markdown")
      withProgress(
        message = "Generating Report...",
        value = 1,
        max = 3,
        {
          params <- list(
            rval_sheet = rval_sheet(),
            rval_sheet_target = rval_sheet_target(),
            name_var = input$select_input_samplenamevar,
            grouping_var = input$select_input_groupingvar,
            donor_var = input$select_input_donorvar,
            normalization_mode = input$select_minfi_norm,
            dropsnps = input$select_minfi_dropsnps,
            dropcphs = input$select_minfi_dropcphs,
            dropsex = input$select_minfi_chromosomes,
            maf = input$slider_minfi_maf,
            probes = rval_gsetprobes(),
            limma_voi = input$select_limma_voi,
            limma_covar = input$checkbox_limma_covariables,
            limma_inter = input$checkbox_limma_interactions,
            limma_arrayweights = input$select_limma_weights,
            limma_ebayes_trend = input$select_limma_trend,
            limma_ebayes_robust = input$select_limma_robust,
            rval_design = rval_design(),
            rval_contrasts = rval_contrasts(),
            rval_voi = rval_voi(),
            rval_dendrogram = rval_dendrogram(),
            min_deltabeta = input$slider_limma_deltab,
            max_fdr = input$slider_limma_adjpvalue,
            max_pvalue = input$slider_limma_pvalue,
            clusteralg = input$select_limma_clusteralg,
            groups2plot = input$select_limma_groups2plot,
            contrasts2plot = input$select_limma_contrasts2plot,
            Colv = input$select_limma_colv,
            distance = input$select_limma_clusterdist,
            scale = input$select_limma_scale,
            removebatch = input$select_limma_removebatch,
            plot_densityplotraw = rval_plot_densityplotraw(),
            plot_densityplot = rval_plot_densityplot(),
            plot_pcaplot = rval_plot_pca()[["graph"]],
            plot_corrplot = rval_plot_corrplot()[["graph"]],
            plot_boxplotraw = rval_plot_boxplotraw(),
            plot_boxplot = rval_plot_boxplot(),
            plot_qcraw = rval_plot_qcraw(),
            plot_bisulfiterawII = rval_plot_bisulfiterawII(),
            plot_sexprediction = rval_plot_sexprediction(),
            plot_snpheatmap = rval_plot_snpheatmap(),
            plot_plotSA = rval_plot_plotSA(),
            table_pcaplot = rval_plot_pca()[["info"]],
            table_corrplot = rval_plot_corrplot()[["info"]],
            data_sexprediction = as.data.frame(minfi::pData(rval_gset()))[["predictedSex"]],
            table_dmps = make_table(),
            filteredlist2heatmap = rval_filteredlist2heatmap()
          )

          # if DMR analysis has been done, we add specific parameters
          if (rval_dmrs_finished()) {
            params[["dmrs_contrasts"]] <- input$select_dmrs_contrasts
            params[["dmrs_rval_dendrogram"]] <- rval_dendrogram_dmrs()
            params[["dmrs_min_deltabeta"]] <- input$slider_dmrs_deltab
            params[["dmrs_max_fdr"]] <- input$slider_dmrs_adjpvalue
            params[["dmrs_max_pvalue"]] <- input$slider_dmrs_pvalue
            params[["dmrs_clusteralg"]] <- input$select_dmrs_clusteralg
            params[["dmrs_groups2plot"]] <- input$select_dmrs_groups2plot
            params[["dmrs_contrasts2plot"]] <- input$select_dmrs_contrasts2plot
            params[["dmrs_regions2plot"]] <- input$select_dmrs_regions2plot
            params[["dmrs_Colv"]] <- input$select_dmrs_colv
            params[["dmrs_distance"]] <- input$select_dmrs_clusterdist
            params[["dmrs_scale"]] <- input$select_dmrs_scale
            params[["dmrs_removebatch"]] <- input$select_dmrs_removebatch
            params[["table_dmrs"]] <- make_table_dmrscount()
            params[["filteredmcsea2heatmap"]] <- rval_filteredmcsea2heatmap()
          }

          newenv <- new.env(parent = globalenv())
          newenv$create_heatmap <- create_heatmap

          render_file <- rmarkdown::render(
            input = system.file("report.Rmd", package = "shinyepico"),
            output_file = file,
            run_pandoc = TRUE,
            params = params,
            envir = newenv
          )

          shinyjs::enable("download_export_markdown")
        }
      )
    }
  )

  # script generation
  output$download_export_script <- downloadHandler(
    filename = "shinyepico_script.R",
    content = function(file) {
      shinyjs::disable("download_export_script")
      withProgress(
        message = "Generating Script...",
        value = 1,
        max = 3,
        {
          fileConn <- file(file)
          writeLines(
            c(
              paste("#This script was generated with shiny\u00C9PICo", utils::packageVersion("shinyepico"), "on", Sys.Date()),
              "library(shiny)",
              "library(data.table)",
              "library(rlang)",
              paste("########################", "PARAMETERS", "########################"),
              "path <- \"insert here the path to the decompress folder with iDATs and the sample_sheet in .csv\"",
              paste("custom_anno <-", rlang::expr_text(input$switch_input_customanno)),
              paste("array <-", rlang::expr_text(gsub(input$select_input_customarray, pattern = "\\|.*", replacement= ""))),
              paste("annotation <-", rlang::expr_text(gsub(input$select_input_customarray, pattern = ".*\\|", replacement= ""))),
              paste("norm_options <-", rlang::expr_text(norm_options)),
              "#Performance",
              paste("cores <-", n_cores),
              "#Data reading",
              paste("sample_name_var <-", rlang::expr_text(input$select_input_samplenamevar)),
              paste("selected_samples <-", rlang::expr_text(input$selected_samples)),
              paste("donorvar <-", rlang::expr_text(input$select_input_donorvar)),
              paste("detectP <-", 0.01),
              "#Normalization",
              paste("normalization_mode <-", rlang::expr_text(input$select_minfi_norm)),
              paste("dropSNPs <-", rlang::expr_text(input$select_minfi_dropsnps)),
              paste("maf <-", rlang::expr_text(input$slider_minfi_maf)),
              paste("dropCpHs <-", rlang::expr_text(input$select_minfi_dropcphs)),
              paste("dropSex <-", rlang::expr_text(input$select_minfi_chromosomes)),
              "#Linear model and contrasts",
              paste("voi_var <-", rlang::expr_text(input$select_limma_voi)),
              paste("covariables <-", rlang::expr_text(input$checkbox_limma_covariables)),
              paste("interactions <-", rlang::expr_text(input$checkbox_limma_interactions)),
              paste("weighting <-", rlang::expr_text(input$select_limma_weights)),
              paste("trend <-", rlang::expr_text(input$select_limma_trend)),
              paste("robust <-", rlang::expr_text(input$select_limma_robust)),
              "#DMP filtering",
              paste("deltaB <-", rlang::expr_text(input$slider_limma_deltab)),
              paste("adjp_max <-", rlang::expr_text(input$slider_limma_adjpvalue)),
              paste("p.value <-", rlang::expr_text(input$slider_limma_pvalue)),
              "#DMP Heatmap",
              paste("removebatch <-", rlang::expr_text(input$select_limma_removebatch)),
              paste("contrasts2plot <-", rlang::expr_text(input$select_limma_contrasts2plot)),
              paste("groups2plot <-", rlang::expr_text(input$select_limma_groups2plot)),
              paste("colv <-", rlang::expr_text(input$select_limma_colv)),
              paste("colsidecolors <-", rlang::expr_text(input$select_limma_colsidecolors)),
              paste("clusteralg <-", rlang::expr_text(input$select_limma_clusteralg)),
              paste("distance <-", rlang::expr_text(input$select_limma_clusterdist)),
              paste("scale <-", rlang::expr_text(input$select_limma_scale)),
              paste("rowsidecolors <-", rlang::expr_text(input$select_limma_rowsidecolors)),
              paste("k_number <-", rlang::expr_text(input$select_limma_knumber)),
              "#DMR calculation",
              paste("mincpgs_dmrs <-", rlang::expr_text(input$slider_dmrs_cpgs)),
              paste("platform <-", rlang::expr_text(input$select_dmrs_platform)),
              paste("permutations_dmrs <-", rlang::expr_text(input$slider_dmrs_permutations)),
              paste("regions_dmrs <-", rlang::expr_text(input$select_dmrs_regions)),
              paste("contrasts_dmrs <-", rlang::expr_text(input$select_dmrs_contrasts)),
              "#DMR filtering",
              paste("fdr_dmrs <-", rlang::expr_text(input$slider_dmrs_adjpvalue)),
              paste("pval_dmrs <-", rlang::expr_text(input$slider_dmrs_pvalue)),
              paste("dif_beta_dmrs <-", rlang::expr_text(input$slider_dmrs_deltab)),
              "#DMR heatmap",
              paste("dmrs_removebatch <-", rlang::expr_text(input$select_dmrs_removebatch)),
              paste("dmrs_contrasts2plot <-", rlang::expr_text(input$select_dmrs_contrasts2plot)),
              paste("dmrs_groups2plot <-", rlang::expr_text(input$select_dmrs_groups2plot)),
              paste("dmrs_regions2plot <-", rlang::expr_text(input$select_dmrs_regions2plot)),
              paste("dmrs_colv <-", rlang::expr_text(input$select_dmrs_colv)),
              paste("dmrs_colsidecolors <-", rlang::expr_text(input$select_dmrs_colsidecolors)),
              paste("dmrs_clusteralg <-", rlang::expr_text(input$select_dmrs_clusteralg)),
              paste("dmrs_distance <-", rlang::expr_text(input$select_dmrs_clusterdist)),
              paste("dmrs_scale <-", rlang::expr_text(input$select_dmrs_scale)),
              paste("dmrs_rowsidecolors <-", rlang::expr_text(input$select_dmrs_rowsidecolors)),
              paste("dmrs_k_number <-", rlang::expr_text(input$select_dmrs_knumber)),
              paste("\n\n\n########################", "FUNCTIONS", "########################"),
              paste("read_idats <-", rlang::expr_text(read_idats)),
              paste("normalize_rgset <-", rlang::expr_text(normalize_rgset)),
              paste("generate_clean_samplesheet <-", rlang::expr_text(generate_clean_samplesheet)),
              paste("generate_contrasts <-", rlang::expr_text(generate_contrasts)),
              paste("generate_design <-", rlang::expr_text(generate_design)),
              paste("generate_limma_fit <-", rlang::expr_text(generate_limma_fit)),
              paste("calculate_global_difs <-", rlang::expr_text(calculate_global_difs)),
              paste("find_dif_cpgs <-", rlang::expr_text(find_dif_cpgs)),
              paste("create_filtered_list <-", rlang::expr_text(create_filtered_list)),
              paste("find_dmrs <-", rlang::expr_text(find_dmrs)),
              paste("add_dmrs_globaldifs <-", rlang::expr_text(add_dmrs_globaldifs)),
              paste("filter_dmrs <-", rlang::expr_text(filter_dmrs)),
              paste("create_dmps_heatdata <-", rlang::expr_text(create_dmps_heatdata)),
              paste("create_dmrs_heatdata <-", rlang::expr_text(create_dmrs_heatdata)),
              paste("create_dendrogram <-", rlang::expr_text(create_dendrogram)),
              paste("create_heatmap <-", rlang::expr_text(create_heatmap)),
              paste("\n\n\n########################", "PIPELINE", "########################"),
              "#Data reading",
              "sheet <- minfi::read.metharray.sheet(path, custom_anno = custom_anno, array = array, annotation = annotation)",
              "sheet_target <- sheet[sheet[[sample_name_var]] %in% selected_samples,]",
              "voi <- factor(make.names(sheet_target[[voi_var]]))",
              "rgset <- read_idats(sheet_target)\n",
              "#Normalization",
              "gset <- normalize_rgset(rgset, normalization_mode, detectP, dropSNPs, maf, dropCpHs, dropSex)",
              "clean_sheet_target <- generate_clean_samplesheet(minfi::pData(gset), donorvar)\n",
              "#Limma model",
              "design <- generate_design(voi_var, sample_name_var, covariables, interactions, clean_sheet_target)",
              "contrasts <- generate_contrasts(voi)",
              "Mvalues <- minfi::getM(gset)",
              "Bvalues <- as.data.frame(minfi::getBeta(gset))",
              "colnames(Bvalues) <- sheet_target[[sample_name_var]]",
              "fit <- generate_limma_fit(Mvalues, design, weighting)\n",
              "#DMPs calculation",
              "global_difs <- calculate_global_difs(Bvalues, voi, contrasts, cores)",
              "ebayes_tables <- find_dif_cpgs(design, fit, contrasts, trend, robust, cores)",
              "filtered_list <- create_filtered_list(ebayes_tables, global_difs, deltaB, adjp_max, p.value, cores)\n",
              "#DMPs heatmap",
              "dmps_heatdata <- create_dmps_heatdata(filtered_list, contrasts2plot, removebatch, design, voi, Bvalues)",
              "if(rowsidecolors) {dendrogram <- create_dendrogram(
                       plot_data = dmps_heatdata, 
                       factorgroups = factor(voi[voi %in% groups2plot], levels = groups2plot),
                       groups2plot = voi %in% groups2plot,
                       clusteralg = clusteralg,
                       distance = distance,
                       scale_selection = scale,
                       k_number = k_number)} else{
                       dendrogram <- NULL
                       }",
              "create_heatmap(
                       plot_data = dmps_heatdata, 
                       factorgroups = factor(voi[voi %in% groups2plot], levels = groups2plot),
                       groups2plot = voi %in% groups2plot,
                       Colv = colv,
                       ColSideColors = colsidecolors,
                       RowSideColors = dendrogram,
                       clusteralg = clusteralg,
                       distance = distance,
                       scale = scale,
                       static = TRUE
                       )\n",
              "#DMRs calculation",
              "mcsea_list <- find_dmrs(ebayes_tables, mincpgs_dmrs, platform, voi, regions_dmrs, contrasts_dmrs, Bvalues, permutations_dmrs, cores)",
              "mcsea_list <- add_dmrs_globaldifs(mcsea_list, global_difs, regions_dmrs)",
              "mcsea_filtered <- filter_dmrs(mcsea_list, fdr_dmrs,pval_dmrs,dif_beta_dmrs,regions_dmrs,contrasts_dmrs)\n",
              "#DMRs heatmap",
              "dmrs_heatdata <- create_dmrs_heatdata(mcsea_filtered, Bvalues, dmrs_regions2plot, dmrs_contrasts2plot, dmrs_removebatch, design, voi)",
              "if(dmrs_rowsidecolors) {dmrs_dendrogram <- create_dendrogram(
                       plot_data = dmrs_heatdata, 
                       factorgroups = factor(voi[voi %in% dmrs_groups2plot], levels = dmrs_groups2plot),
                       groups2plot = voi %in% dmrs_groups2plot,
                       clusteralg = dmrs_clusteralg,
                       distance = dmrs_distance,
                       scale_selection = dmrs_scale,
                       k_number = dmrs_k_number)} else{
                       dmrs_dendrogram <- NULL
                       }",
              "create_heatmap(
                       plot_data = dmrs_heatdata, 
                       factorgroups = factor(voi[voi %in% dmrs_groups2plot], levels = dmrs_groups2plot),
                       groups2plot = voi %in% dmrs_groups2plot,
                       Colv = dmrs_colv,
                       ColSideColors = dmrs_colsidecolors,
                       RowSideColors = dmrs_dendrogram,
                       clusteralg = dmrs_clusteralg,
                       distance = dmrs_distance,
                       scale = dmrs_scale,
                       static = TRUE
                       )"
            ),
            fileConn
          )
          close(fileConn)

          shinyjs::enable("download_export_script")
        }
      )
    }
  )


  # custom heatmap
  output$download_export_heatmaps <- downloadHandler(
    "Custom_heatmap.pdf",
    content = function(file) {
      shinyjs::disable("download_export_heatmaps")
      withProgress(
        message = "Generating plot...",
        value = 1,
        max = 4,
        {
          grDevices::pdf(
            file = file,
            height = 11.71,
            width = 8.6
          )
          if (input$select_export_heatmaptype == "DMPs") {
            create_heatmap(
              rval_filteredlist2heatmap(),
              factorgroups = factor(rval_voi()[rval_voi() %in% input$select_limma_groups2plot],
                levels = input$select_limma_groups2plot
              ),
              groups2plot = rval_voi() %in% input$select_limma_groups2plot,
              Colv = as.logical(input$select_limma_colv),
              ColSideColors = as.logical(input$select_limma_colsidecolors),
              RowSideColors = rval_dendrogram(),
              clusteralg = input$select_limma_clusteralg,
              distance = input$select_limma_clusterdist,
              scale = input$select_limma_scale,
              static = TRUE
            )
          }
          else {
            create_heatmap(
              rval_filteredmcsea2heatmap(),
              factorgroups = factor(rval_voi()[rval_voi() %in% input$select_dmrs_groups2plot],
                levels = input$select_dmrs_groups2plot
              ),
              groups2plot = rval_voi() %in% input$select_dmrs_groups2plot,
              Colv = as.logical(input$select_dmrs_colv),
              ColSideColors = as.logical(input$select_dmrs_colsidecolors),
              RowSideColors = rval_dendrogram_dmrs(),
              clusteralg = input$select_dmrs_clusteralg,
              distance = input$select_dmrs_clusterdist,
              scale = input$select_dmrs_scale,
              static = TRUE
            )
          }

          grDevices::dev.off()
          shinyjs::enable("download_export_heatmaps")
        }
      )
    }
  )
}
omorante/shinyepico documentation built on May 11, 2023, 7:21 p.m.