shinyapps.io/LFApp Mobile Analysis/app.R

library(shiny)
library(shinyMobile)
library(EBImage)
library(DT)
library(ggplot2)

ui <- f7Page(
  allowPWA=TRUE,
  title = "LFA mobile app",
  f7TabLayout(
    # Maybe the navbar will be removed later.
    navbar = f7Navbar(
      title="LFApp mobile analysis"
    ),
    # Each tab has it's own content
    f7Tabs(
      animated = TRUE,
      id = "tabs",
      f7Tab(
        title = "Crop & Segmentation",
        tabName = "CropSegmentation",
        icon = f7Icon("tray_arrow_up"),
        active = TRUE,
        # Upload file block
        f7Block(
          strong = TRUE,
          inset = FALSE,
          f7Radio(inputId= "upload", label="Upload image or choose sample", choices=list("Upload image", "Sample"), selected = "Sample"),
          conditionalPanel(
            condition = "input.upload == 'Upload image'",
            f7File(inputId = 'file1',
                   label = 'Upload Image',
                   placeholder = 'JPEG, PNG, and TIFF are supported',
                   accept = c(
                     "image/jpeg",
                     "image/x-png",
                     "image/tiff",
                     ".jpg",
                     ".png",
                     ".tiff"))
            )
        ),
        f7Accordion(
          f7AccordionItem(
            title = "Rotate Image",
            tagList(
              f7Slider("rotate", label="Angle",
                          min=-45, max=45, value=0, scale=TRUE),
              br(),
              f7Segment(
                f7Button(inputId="rotateCCW", label = "-90"),
                f7Button(inputId="rotateCW", label = "+90"),
                f7Button(inputId="fliphor", label = "FH"),
                f7Button(inputId="flipver", label = "FV")
              )
            )
          )
        ),
        f7Block(
          f7BlockTitle("Set number of strips and number of lines per strip"),
          strong = TRUE,
          inset = FALSE,
          f7Slider("strips", label="Number of strips", min=1, max=10, value=1, scale=TRUE, scaleSteps=9),
          f7Slider("bands", label="Number of lines", min=2, max=6, value=2, scale=TRUE, scaleSteps=4)
        ),
        f7Block(
          f7BlockTitle("Cropping and Segmentation", size="medium"),
          strong = TRUE,
          inset = FALSE,
          # The content of the tab goes below this
          plotOutput("plot1",
                     click = "plot_click",
                     dblclick = "plot_dblclick",
                     hover = hoverOpts("plot_hover", delay = 5000, clip = TRUE),
                     brush = "plot_brush"),
          h5("Click and drag to select a region of interest. Double click on the selected region to zoom.", align = "center"),
          uiOutput("cropButtons"),
          tags$head(
            tags$script("
              $(document).ready(function() {
                var plot = document.getElementById('plot1')

                plot.addEventListener('touchmove', function (e) {
                  var touch = e.changedTouches[0];
                  var mouseEvent = new MouseEvent('mousemove', {
                    view: window,
                    bubbles: true,
                    cancelable: true,
                    screenX: touch.screenX,
                    screenY: touch.screenY,
                    clientX: touch.clientX,
                    clientY: touch.clientY
                  })
                  touch.target.dispatchEvent(mouseEvent);
                  e.preventDefault()
                }, { passive: false });

                plot.addEventListener('touchstart', function(e) {
                  var touch = e.changedTouches[0];
                  var mouseEvent = new MouseEvent('mousedown', {
                    view: window,
                    bubbles: true,
                    cancelable: true,
                    screenX: touch.screenX,
                    screenY: touch.screenY,
                    clientX: touch.clientX,
                    clientY: touch.clientY
                  })
                  touch.target.dispatchEvent(mouseEvent);
                  e.preventDefault()
                }, { passive: false });

                plot.addEventListener('touchstart', function(e) {
                  var touch = e.changedTouches[0];
                  var mouseEvent = new MouseEvent('click', {
                    view: window,
                    bubbles: true,
                    cancelable: true,
                    screenX: touch.screenX,
                    screenY: touch.screenY,
                    clientX: touch.clientX,
                    clientY: touch.clientY
                  })
                  touch.target.dispatchEvent(mouseEvent);
                  e.preventDefault()
                }, { passive: false });

                plot.addEventListener('touchend', function(e) {
                  var touch = e.changedTouches[0];
                  var mouseEvent = new MouseEvent('mouseup', {
                    view: window,
                    bubbles: true,
                    cancelable: true,
                    screenX: touch.screenX,
                    screenY: touch.screenY,
                    clientX: touch.clientX,
                    clientY: touch.clientY
                  })
                  touch.target.dispatchEvent(mouseEvent);
                  e.preventDefault()
                }, { passive: false });
              })
            "),
            tags$style("#plot1 { touch-action: none; }")
          )
        )
      ),
      f7Tab(
        title = "Background",
        tabName = "Background",
        icon = f7Icon("circle_lefthalf_fill"),
        active = FALSE,
        f7Block(
          strong = TRUE,
          inset = FALSE,
          "Select strip: ",
          f7Stepper("selectStrip", label = "", min=1, max=1, value=1, size="small"),
          f7Accordion(
            f7AccordionItem(
              title = "Color image?",
              f7Radio("channel", label="Conversion mode", choices=list("luminance", "gray", "red", "green", "blue"), selected = "luminance"),
            ),
          ),
          f7Radio("invert", label="Lines darker than background?", choices=list("No", "Yes"), selected = "No"),
          f7Radio("thresh", label="Threshold", choices=list("Otsu", "Quantile", "Triangle", "Li"), selected = "Otsu"),
          conditionalPanel(
            condition = "input.thresh == 'Quantile'",
            f7Stepper(inputId = "quantile1",
                     label = "Probability [%]:",
                     value = 99,
                     min = 0,
                     max = 100,
                     step = 0.5,
                     manual = TRUE)
            ),
          conditionalPanel(
            condition = "input.thresh == 'Triangle'",
            f7Stepper(inputId = "tri_offset",
                     label = "Offset:",
                     value = 0.2,
                     min = 0,
                     max = 1,
                     step = 0.1,
                     manual = TRUE)
          ),
          f7Segment(
            f7Button(inputId="threshold", label = "Apply Threshold")
          )
        ),
        uiOutput("threshPlots"),
      ),
      f7Tab(
        title = "Intensity Data",
        tabName = "IntensityData",
        icon = f7Icon("table"),
        active = FALSE,
        f7Block(
          f7Accordion(
            f7AccordionItem(
              title = "Upload existing intensity data",
              f7File(inputId = 'intensFile',
                     label = 'Select CSV file',
                     accept = c("text/csv",
                                "text/comma-separated-values,text/plain",
                                ".csv"))
            )
        ),
        f7Block(
          strong = TRUE,
          f7Block(
            style = "overflow-x:scroll",
            DTOutput("intens")
          ),
          f7Segment(
            f7DownloadButton("downloadData", label = "Download Data"),
            f7Button("deleteData", color="red", label="Delete Data")
          ),
          f7Button(inputId="expInfo", label="Switch to experiment info")
          )
        )
      ),
      f7Tab(
        title = "Experiment Info",
        tabName = "ExperimentInfo",
        icon = f7Icon("info_circle"),
        active = FALSE,
        f7Block(
          f7Accordion(
            f7AccordionItem(
              title = "Upload experiment info or upload existing merged data",
              f7Accordion(
                f7File(inputId = 'expFile',
                       label = 'Select CSV file',
                       accept = c("text/csv",
                                  "text/comma-separated-values,text/plain",
                                  ".csv")),
                f7AccordionItem(
                  title = "CSV options",
                  f7Checkbox("header", "Header", TRUE),
                  f7Radio("sep", "Separator",
                          choices = c("Comma ( , )",
                                      "Semicolon ( ; )",
                                      "Tab ( \\t )"),
                          selected = "Comma ( , )"),
                  f7Radio("quote", "Quote",
                          choices = c("None",
                                      'Double Quote ( " )',
                                      "Single Quote ( ' )"),
                          selected = "None"))
                )
              ),
            f7AccordionItem(
              title = "Merge datasets",
              "Select ID columns and merge datasets",
              f7Text("mergeIntens", label = "ID Column Intensity Data", value = "File"),
              f7Text("mergeExp", label = "ID Column Experiment Info", value = "File"),
              f7Segment(
                f7Button("merge", label = "Merge With Intensity Data")
              )
            ),
          ),
          f7Block(
            strong = TRUE,
            f7Block(
              strong = TRUE,
              style = "overflow-x:scroll",
              DTOutput("experiment")
            ),
            f7Segment(
              f7DownloadButton("downloadData2", label = "Download Data"),
              f7Button("deleteData2", color="red", label="Delete Data")

            ),
            f7Block(
              f7Button("prepare", label = "Prepare Calibration")
            )
          )
        )
      ),
      f7Tab(
        title = "Calibration",
        tabName = "Calibration",
        icon = f7Icon("graph_square"),
        active = FALSE,
        f7Block(
          f7Accordion(
            f7AccordionItem(
              title = "Upload existing data for calibration",
              f7File(inputId = 'prepFile',
                     label = 'Select CSV file',
                     accept = c("text/csv",
                                "text/comma-separated-values,text/plain",
                                ".csv"))
            ),
            f7AccordionItem(
              title = "Furter preprocessing steps",
              f7Radio("radioPrepro",
                      label = "",
                      choices = c("None",
                                  "Average technical replicates",
                                  "Reshape from long to wide"),
                      selected = "None"),
              conditionalPanel(
                condition = "input.radioPrepro == 'Average technical replicates'",
                f7Text("combRepsColSI", label = "Column with sample information:", value = "Sample"),
                f7Stepper(inputId = "colorsBands",
                          label = "Number of analytes/colors per line:",
                          value = 1,
                          min = 1,
                          max = 5,
                          step = 1,
                ),
                conditionalPanel(
                  condition = "input.colorsBands > 1",
                  f7Text("combRepsColCL", label = "Column with color information:", value = "Color"),
                ),
                f7Radio("radioReps",
                        label = ("Choose measure for averaging:"),
                        choices = list("Mean",
                                       "Median"),
                        selected = "Mean"),
                f7Segment(
                  f7Button("combReps", label = "Average Technical Replicates")
                )
              ),
              conditionalPanel(
                condition = "input.radioPrepro == 'Reshape from long to wide'",
                f7Text("reshapeCol", label = "Column:", value = "Color"),
                f7Segment(
                  f7Button("reshapeWide", label = "Reshape")
                )
              )
            ),
            f7AccordionItem(
              title = "Calibration",
              open = TRUE,
              f7Text("analysisName","Analysis name:", value="Model1"),
              f7Radio("chosenModel",
                      label = "Choose model",
                      choices = list("Linear model (lm)",
                                     "Local polynomial model (loess)",
                                     "Generalized additive model (gam)"),
                      selected = "Linear model (lm)"),
              f7TextArea("respVar", label = "Specify response variable (R expresssion)"),
              f7TextArea("subset", label = "Optional: specify subset (logical R expression)"),
              f7Text("concVar", label = "Specify column with concentration"),
              # f7Text("concVar", "Specify column with concentration"),
              f7Checkbox("useLog", "Logarithmize concentration", value=FALSE),
              f7Block(
                strong = TRUE,
                f7Block(
                  strong = TRUE,
                  style = "overflow-x:scroll",
                  DTOutput("calibration")
                ),
                f7Segment(
                  f7DownloadButton("downloadData3", label = "Download Data"),
                  f7Button("deleteData3", color="red", label="Delete Data")
                )
              ),
              f7Segment(
                f7DownloadButton("runCali", label = "Run Calibration Analysis")
              )
            )
          ),
        )
      ),
      f7Tab(
        title = "Results",
        tabName = "Results",
        icon = f7Icon("doc_text_search"),
        active = FALSE,
        uiOutput("results"),
        uiOutput("saveModelButton")
      ),
      f7Tab(
        title = "Quantification",
        tabName = "Quantification",
        icon = f7Icon("gauge"),
        active = FALSE,
        f7Block(
          strong = TRUE,
          inset = FALSE,
          f7Radio(inputId= "quanUpload",
                  label="You can use Intensity Data or upload new data",
                  choices=list("Use Intensity Data", "Upload Data"),
                  selected = "Upload Data"),
          conditionalPanel(
            condition = "input.quanUpload == 'Upload Data'",
            f7File(inputId = 'quanData',
                   label = 'Upload Image',
                   accept = c("csv"))
          )
        ),
        f7Block(
          strong = TRUE,
          h3("Load existing model from file"),
          f7File(inputId = 'model',
                 label = 'Select model',
                 accept = ".rds",
                 placeholder = "RDS file"),
          h3("Predict concentration"),
          f7Button("predict", label = "Predict"),
          h3("Download concentration data"),
          f7DownloadButton("downloadData4", "Download data")
        ),
        f7Block(
          strong = TRUE,
          style = "overflow-x:scroll",
          DTOutput("quant")
        )
      )
    )
  )
)

triangle <- function(image, offset = 0.2, breaks = 256) {
  if(!is(image, "Image"))
    stop("'image' must be of class 'Image'!")
  breaks <- as.integer(breaks)
  stopifnot(offset >= 0)
  stopifnot(breaks > 0)

  ## compute histogram and extract counts and breaks
  rg <- range(image)
  bins <- breaks
  breaks <- seq(rg[1], rg[2], length = breaks + 1L)
  image.hist <- hist.default(imageData(image), breaks = breaks, plot = FALSE)
  hist.counts <- image.hist$counts
  hist.breaks <- image.hist$breaks

  ## centers of bins
  delta <- hist.breaks[2]-hist.breaks[1]
  hist.bins <- hist.breaks[-(bins+1)] + delta/2

  ## location of peaks and peak value
  ind.peaks <- which(hist.counts == max(hist.counts))
  ind.first.peak <- ind.peaks[1]
  ind.last.peak <- ind.peaks[length(ind.peaks)]
  peak.height <- hist.counts[ind.first.peak]

  ## fist and last bin with positive count
  pos.counts <- which(hist.counts > 0)
  ind.low <- pos.counts[1]
  ind.high <- pos.counts[length(pos.counts)]

  if((ind.first.peak - ind.low) < (ind.high - ind.last.peak)){
    ## right tail is longer
    sel <- ind.last.peak:ind.high
    norm.counts <- hist.counts[sel]/peak.height
    norm.bins <- (1:length(sel))/length(sel)
    distances <- (1-norm.counts)*(1-norm.bins)/sqrt((1-norm.counts)^2 + (1-norm.bins)^2)
  }else{
    ## left tail is longer
    sel <- ind.low:ind.first.peak
    norm.counts <- hist.counts[sel]/peak.height
    norm.bins <- (1:length(sel))/length(sel)
    distances <- (1-norm.counts)*norm.bins/sqrt((1-norm.counts)^2 + norm.bins^2)
  }
  ind.max <- which.max(distances)

  hist.bins[sel[ind.max]] + offset
}

threshold_li <- function(image, tolerance=NULL, initial_guess=NULL, iter_callback=NULL) {
  # For Li's algorithm to work, the image should be positive
  image_min <- min(image)
  image <- image - image_min
  # Tolerance has to be positive or there is a risk of while loop to be infinite.
  if (is.null(tolerance)) {
    tolerance <- abs(min(diff(as.vector(image))) / 2)
  }
  # Initial estimate for iterations
  if (is.null(initial_guess)) {
    t_next <- mean(image)
  } else if (class(initial_guess)=="function"){
    t_next <- initial_guess(image)
  } else if (is.numeric(initial_guess)) {
    t_next <- initial_guess - image_min
    image_max <- max(image) + image_min
    if (!t_next>0 & !t_next<max(image)) {
      stop(sprintf("The threshold_li must be greater than 0 and lesser than max
                   value of the image. threshold_li is %s", threshold_li))
    } else {
      stop("The initial_guess has incorrect class. It should be numeric or a
           function that returns numeric value.")
    }
  }
  # The difference between t_next and t_curr should be equal to the tolerance.
  t_curr <- tolerance * (-2)

  if (!is.null(iter_callback)) {
    iter_callback(t_next + image_min)
  }

  # Stop the iteration when the difference between the new and old threshold
  # value is less than the tolerance
  while(abs(t_next - t_curr) > tolerance) {
    t_curr <- t_next
    foreground <- (image > t_curr)
    mean_fore <- mean(image[foreground])
    mean_back <- mean(image[!foreground])

    t_next <- ((mean_back - mean_fore) /
                 (log(mean_back) - log(mean_fore)))

    if (!is.null(iter_callback)) {
      iter_callback(t_next + image_min)
    }
  }
  threshold <- t_next + image_min
  threshold
}

server <- function(input, output, session){
  ########## FIRST TAB

  options(shiny.maxRequestSize=50*1024^2) #file can be up to 50 mb; default is 5 mb
  shinyImageFile <- reactiveValues(shiny_img_origin = NULL, shiny_img_cropped = NULL,
                                   shiny_img_final = NULL, Threshold = NULL)
  IntensData <- NULL
  predFunc <- NULL
  ExpInfo <- NULL
  MergedData <- NULL
  fit <- NULL
  modelPlot <- NULL
  LOB <- NULL
  LOD <- NULL
  LOQ <- NULL
  calFun <- NULL

  # checks upload for file imput
  observe({
    #default: upload image
    if(input$upload == "Upload image"){
      output$plot1 <- renderPlot({
        if(is.null(input$file1))
          return(NULL)
        validate(need(!is.null(input$file1), "Must upload a valid jpg, png, or tiff"))
      })
    }
    if(input$upload == "Sample"){
      # using sample image
      img <- readImage("sample.TIF")
      shinyImageFile$shiny_img_origin <- img
      shinyImageFile$shiny_img_cropped <- img
      shinyImageFile$shiny_img_final <- img

      shinyImageFile$filename <- "sample.TIF"
      #outputs image to plot1 -- main plot
      output$plot1 <- renderPlot({ EBImage::display(shinyImageFile$shiny_img_final, method = "raster") })
    }
    drawCropButtons()
  })

  # if the new file is entered, it will become new ImageFile
  observeEvent(input$file1, {
    shinyImageFile$filename <- input$file1$name
    img <- readImage(renameUpload(input$file1))
    shinyImageFile$shiny_img_origin <- img
    shinyImageFile$shiny_img_cropped <- img
    shinyImageFile$shiny_img_final <- img
    output$plot1 <- renderPlot({EBImage::display(img, method = "raster")})
  })

  #the datapath is different from the one needed to properly recognize photo
  #so this function renames the file
  renameUpload <- function(inFile){
    if(is.null(inFile))
      return(NULL)

    oldNames <- inFile$datapath
    newNames <- file.path(dirname(inFile$datapath), inFile$name)
    file.rename(from = oldNames, to = newNames)
    inFile$datapath <- newNames

    return(inFile$datapath)
  }

  drawCropButtons <- function(resetButton=FALSE, segButton=FALSE) {
    output$cropButtons <- renderUI({
      tagList(
        f7Segment(
          if (resetButton) {
            f7Button("reset", color = "blue", label = "Reset")
          } else {
            f7Button("no-reset", color = "gray", label = "Reset")
          },
          if (segButton) {
            f7Button("segmentation", color="green", label = "Apply Segmentation")
          } else {
            f7Button("no-segmentation", color="gray", label = "Apply Segmentation")
          }
        )
      )
    })
  }

  # Rotation ----------------------------------------------------------------

  observe({reactiveRotation()})

  reactiveRotation <- eventReactive(input$rotate, {
    isolate({
      if (!is.null(shinyImageFile$shiny_img_cropped)) {
        shinyImageFile$shiny_img_final <- EBImage::rotate(shinyImageFile$shiny_img_cropped, input$rotate,
                                                          bg.col="white")
        output$plot1 <- renderPlot({EBImage::display(shinyImageFile$shiny_img_final, method = "raster")})
        session$resetBrush("plot_brush")
      }
    })
  })

  observe({reactiveRotationCCW()})

  reactiveRotationCCW <- eventReactive(input$rotateCCW, {
    isolate({
      if (!is.null(shinyImageFile$shiny_img_cropped)) {
        shinyImageFile$shiny_img_cropped <- EBImage::rotate(shinyImageFile$shiny_img_cropped, -90)
        shinyImageFile$shiny_img_final <- shinyImageFile$shiny_img_cropped
        output$plot1 <- renderPlot({EBImage::display(shinyImageFile$shiny_img_final, method = "raster")})
        session$resetBrush("plot_brush")
      }
    })
  })

  observe({reactiveRotationCW()})

  reactiveRotationCW <- eventReactive(input$rotateCW, {
    isolate({
      if (!is.null(shinyImageFile$shiny_img_cropped)) {
        shinyImageFile$shiny_img_cropped <- EBImage::rotate(shinyImageFile$shiny_img_cropped, 90)
        shinyImageFile$shiny_img_final <- shinyImageFile$shiny_img_cropped
        output$plot1 <- renderPlot({EBImage::display(shinyImageFile$shiny_img_final, method = "raster")})
        session$resetBrush("plot_brush")
      }
    })
  })

  observe({reactiveRotationFlip()})

  reactiveRotationFlip <- eventReactive(input$fliphor, {
    isolate({
      if (!is.null(shinyImageFile$shiny_img_cropped)) {
        shinyImageFile$shiny_img_cropped <- EBImage::flip(shinyImageFile$shiny_img_cropped)
        shinyImageFile$shiny_img_final <- shinyImageFile$shiny_img_cropped
        output$plot1 <- renderPlot({EBImage::display(shinyImageFile$shiny_img_final, method = "raster")})
        session$resetBrush("plot_brush")
      }
    })
  })

  observe({reactiveRotationFlop()})

  reactiveRotationFlop <- eventReactive(input$flipver, {
    isolate({
      if (!is.null(shinyImageFile$shiny_img_cropped)) {
        shinyImageFile$shiny_img_cropped <- EBImage::flop(shinyImageFile$shiny_img_cropped)
        shinyImageFile$shiny_img_final <- shinyImageFile$shiny_img_cropped
        output$plot1 <- renderPlot({EBImage::display(shinyImageFile$shiny_img_final, method = "raster")})
        session$resetBrush("plot_brush")
      }
    })
  })

  croppedImage <- function(image, xmin, ymin, xmax, ymax){
    if(length(dim(image)) == 2)
      image <- image[xmin:xmax, ymin:ymax, drop = FALSE]
    else if(length(dim(image)) == 3)
      image <- image[xmin:xmax, ymin:ymax, ,drop = FALSE]
    return(image)
  }

  observe({resetImage()})

  resetImage <- eventReactive(input$reset,{
    isolate({
      shinyImageFile$shiny_img_cropped <- shinyImageFile$shiny_img_origin
      shinyImageFile$shiny_img_final <- shinyImageFile$shiny_img_cropped
      output$plot1 <- renderPlot({EBImage::display(shinyImageFile$shiny_img_final, method = "raster")})
      session$resetBrush("plot_brush")
      updateSliderInput(session, "rotate", value=0)
      drawCropButtons()
    })
  })


  #prompts shiny to look at recursive crop
  observe({recursiveCrop()})

  #only executes when selection box is double clicked
  recursiveCrop <- eventReactive(input$plot_dblclick,{
    isolate({
      p <- input$plot_brush
      validate(need(p$xmax <= dim(shinyImageFile$shiny_img_cropped)[1],
                    "Highlighted portion is out of bounds on the x-axis"))
      validate(need(p$ymax <= dim(shinyImageFile$shiny_img_cropped)[2],
                    "Highlighted portion is out of bounds on the y-axis"))
      validate(need(p$xmin >= 0,
                    "Highlighted portion is out of bounds on the x-axis"))
      validate(need(p$ymin >= 0,
                    "Highlighted portion is out of bounds on the y-axis"))
      shinyImageFile$shiny_img_cropped <- croppedImage(shinyImageFile$shiny_img_final, p$xmin, p$ymin, p$xmax, p$ymax)
      shinyImageFile$shiny_img_final <- shinyImageFile$shiny_img_cropped
      output$plot1 <- renderPlot({
        EBImage::display(shinyImageFile$shiny_img_final, method = "raster")
      })
      updateF7Slider("rotate", value=0)
      session$resetBrush("plot_brush")
      drawCropButtons(resetButton = TRUE)
    })
    session$resetBrush("plot_brush")
  })


  observe({recursiveGrid()})

  recursiveGrid <- eventReactive(input$plot_brush,{
    isolate({
      p <- input$plot_brush
      output$plot1 <- renderPlot({
        EBImage::display(shinyImageFile$shiny_img_final, method = "raster")

        colcuts <- seq(p$xmin, p$xmax, length.out = input$strips + 1)
        rowcuts <- seq(p$ymin, p$ymax, length.out = 2*input$bands) # bands + spaces between bands

        for (x in colcuts) {
          lines(x = rep(x, 2), y = c(p$ymin, p$ymax), col="red")
        }
        for (y in rowcuts) {
          lines(x = c(p$xmin, p$xmax), y = rep(y, 2), col="red")
        }
      })
      drawCropButtons(segButton = TRUE, resetButton = TRUE)
    })
  })


  observe({recursiveSegmentation()})

  #only executes when Apply Segmentation is clicked
  recursiveSegmentation <- eventReactive(input$segmentation,{
    isolate({
      p <- input$plot_brush
      # Check if the region of interest is out of the bounds
      if (p$xmax <= dim(shinyImageFile$shiny_img_cropped)[1] &&
          p$ymax <= dim(shinyImageFile$shiny_img_cropped)[2] &&
          p$xmin >= 0 &&
          p$ymin >= 0) {
            MAX <- dim(shinyImageFile$shiny_img_cropped)[1:2]
            colcuts <- seq(p$xmin, p$xmax, length.out = input$strips + 1)
            rowcuts <- seq(p$ymin, p$ymax, length.out = 2*input$bands)

            segmentation.list <- vector("list", length = input$strips)
            count <- 0
            for(i in 1:input$strips){
              tmp.list <- vector("list", length = 2*input$bands-1)
              for(j in 1:(2*input$bands-1)){
                img <- shinyImageFile$shiny_img_final
                if(length(dim(img)) == 2)
                  img <- img[colcuts[i]:colcuts[i+1], rowcuts[j]:rowcuts[j+1]]
                else if(length(dim(img)) == 3)
                  img <- img[colcuts[i]:colcuts[i+1], rowcuts[j]:rowcuts[j+1], , drop = FALSE]
                tmp.list[[j]] <- img
              }
              segmentation.list[[i]] <- tmp.list
            }
            shinyImageFile$cropping_grid <- list("columns" = colcuts, "rows" = rowcuts)
            shinyImageFile$segmentation_list <- segmentation.list
            updateF7Tabs(session=session, id="tabs", selected = "Background")
      } else {
        f7Toast(text="Error: The grid is out of bounds", position="bottom", session=session)
      }
    })
  })

  ################ END OF FIRST TAB

  ############## SECOND TAB
  observe({
    input$thresh
    updateF7Stepper("selectStrip", max=input$strips)
  })

  showThresholdPlots <- function() {
    output$threshPlots <- renderUI({
      tagList(
        f7Block(
          strong = TRUE,
          inset = FALSE,
          h3('Background Correction', align = "center"),
          verbatimTextOutput("thresh"),
          h4('Signal Intensity Above Background', align = "center"),
          plotOutput("plot3"),
          h4('Lines After Background Subtraction', align = "center"),
          plotOutput("plot4"),
          verbatimTextOutput("meanIntens"),
          verbatimTextOutput("medianIntens"),
          f7Segment(
            f7Button("data", color="green", label = "Add to Data"),
            f7Button("showIntensData", label = "Switch To Intensity Data")
          )
        )
      )
    })
  }

  observe({recursiveThreshold()})

  recursiveThreshold <- eventReactive(input$threshold,{
    isolate({
      showThresholdPlots()
      seg.list <- shinyImageFile$segmentation_list
      i <- input$selectStrip
      if(input$thresh == "Quantile"){
        Background <- vector(mode = "list", length = input$bands)
        for(j in 1:input$bands){
          img <- seg.list[[i]][[j]]
          if(colorMode(img) > 0){
            img <- 1-channel(img, input$channel)
          }
          if(input$invert=="Yes") {
            img <- 1 - img
          }
          Background[[j]] <- as.numeric(EBImage::imageData(img))
        }
        Background.Threshold <- quantile(unlist(Background),
                                         probs = input$quantile1/100)
        shinyImageFile$Threshold <- Background.Threshold
        output$plot3 <- renderPlot({
          par(mfcol = c(1, input$bands))
          Bands <- seq(1, 2*input$bands-1, by = 2)
          count <- 0
          for(j in Bands){
            count <- count + 1
            img <- seg.list[[i]][[j]]
            if(colorMode(img) > 0){
              img <- 1-channel(img, input$channel)
            }
            if(input$invert=="Yes") {
              img <- 1 - img
            }
            signal <- EBImage::imageData(img) > Background.Threshold
            EBImage::imageData(img) <- signal
            plot(img)
            title(paste0("Line ", count))
          }
        })
        shinyImageFile$Mean_Intensities <- matrix(0, nrow = 1, ncol = input$bands)
        shinyImageFile$Median_Intensities <- matrix(0, nrow = 1, ncol = input$bands)
        output$plot4 <- renderPlot({
          par(mfcol = c(1, input$bands))
          Bands <- seq(1, 2*input$bands-1, by = 2)
          count <- 0
          for(j in Bands){
            count <- count + 1
            img <- seg.list[[i]][[j]]
            if(colorMode(img) > 0){
              img <- 1-channel(img, input$channel)
            }
            if(input$invert=="Yes") {
              img <- 1 - img
            }
            signal <- EBImage::imageData(img) > Background.Threshold
            EBImage::imageData(img) <- (EBImage::imageData(img) - Background.Threshold)*signal
            shinyImageFile$Mean_Intensities[1,count] <- mean(EBImage::imageData(img)[signal])
            shinyImageFile$Median_Intensities[1,count] <- median(EBImage::imageData(img)[signal])
            plot(img)
            title(paste0("Line ", count))
          }
        })
      }
      else if(input$thresh == "Otsu"){
        Background.Threshold <- numeric(input$bands)
        output$plot3 <- renderPlot({
          par(mfcol = c(1, input$bands))
          count1 <- 0
          Bands <- seq(1, 2*input$bands-1, by = 2)
          count2 <- 0
          for(j in Bands){
            count1 <- count1 + 1
            count2 <- count2 + 1
            img <- seg.list[[i]][[j]]
            if(colorMode(img) > 0){
              img <- 1-channel(img, input$channel)
            }
            if(input$invert=="Yes") {
              img <- 1 - img
            }
            Background.Threshold[count1] <- otsu(img)
            signal <- EBImage::imageData(img) > Background.Threshold[count1]
            EBImage::imageData(img) <- signal
            plot(img)
            title(paste0("Line ", count2))
          }
          shinyImageFile$Threshold <- Background.Threshold
        })
        shinyImageFile$Mean_Intensities <- matrix(0, nrow = 1, ncol = input$bands)
        shinyImageFile$Median_Intensities <- matrix(0, nrow = 1, ncol = input$bands)
        output$plot4 <- renderPlot({
          par(mfcol = c(1, input$bands))
          count1 <- 0
          Bands <- seq(1, 2*input$bands-1, by = 2)
          count2 <- 0
          for(j in Bands){
            count1 <- count1 + 1
            count2 <- count2 + 1
            img <- seg.list[[i]][[j]]
            if(colorMode(img) > 0){
              img <- 1-channel(img, input$channel)
            }
            if(input$invert=="Yes") {
              img <- 1 - img
            }
            thr <- otsu(img)
            signal <- EBImage::imageData(img) > thr
            EBImage::imageData(img) <- (EBImage::imageData(img) - thr)*signal
            shinyImageFile$Mean_Intensities[1,count1] <- mean(EBImage::imageData(img)[signal])
            shinyImageFile$Median_Intensities[1,count1] <- median(EBImage::imageData(img)[signal])
            plot(img)
            title(paste0("Line ", count2))
          }
        })
      }
      else if(input$thresh == "Triangle"){
        Background.Threshold <- numeric(input$bands)
        output$plot3 <- renderPlot({
          par(mfcol = c(1, input$bands))
          count1 <- 0
          Bands <- seq(1, 2*input$bands-1, by = 2)
          count2 <- 0
          for(j in Bands){
            count1 <- count1 + 1
            count2 <- count2 + 1
            img <- seg.list[[i]][[j]]
            if(colorMode(img) > 0){
              img <- 1-channel(img, input$channel)
            }
            if(input$invert=="Yes") {
              img <- 1 - img
            }
            Background.Threshold[count1] <- triangle(img, input$tri_offset)
            signal <- EBImage::imageData(img) > Background.Threshold[count1]
            EBImage::imageData(img) <- signal
            plot(img)
            title(paste0("Line ", count2))
          }
          shinyImageFile$Threshold <- Background.Threshold
        })
        shinyImageFile$Mean_Intensities <- matrix(0, nrow = 1, ncol = input$bands)
        shinyImageFile$Median_Intensities <- matrix(0, nrow = 1, ncol = input$bands)
        output$plot4 <- renderPlot({
          par(mfcol = c(1, input$bands))
          count1 <- 0
          Bands <- seq(1, 2*input$bands-1, by = 2)
          count2 <- 0
          for(j in Bands){
            count1 <- count1 + 1
            count2 <- count2 + 1
            img <- seg.list[[i]][[j]]
            if(colorMode(img) > 0){
              img <- 1-channel(img, input$channel)
            }
            if(input$invert=="Yes") {
              img <- 1 - img
            }
            thr <- triangle(img, input$tri_offset)
            signal <- EBImage::imageData(img) > thr
            EBImage::imageData(img) <- (EBImage::imageData(img) - thr)*signal
            shinyImageFile$Mean_Intensities[1,count1] <- mean(EBImage::imageData(img)[signal])
            shinyImageFile$Median_Intensities[1,count1] <- median(EBImage::imageData(img)[signal])
            plot(img)
            title(paste0("Line ", count2))
          }
        })
      }
      else if(input$thresh == "Li"){
        Background.Threshold <- numeric(input$bands)
        output$plot3 <- renderPlot({
          par(mfcol = c(1, input$bands))
          count1 <- 0
          Bands <- seq(1, 2*input$bands-1, by = 2)
          count2 <- 0
          for(j in Bands){
            count1 <- count1 + 1
            count2 <- count2 + 1
            img <- seg.list[[i]][[j]]
            if(colorMode(img) > 0){
              img <- 1-channel(img, input$channel)
            }
            if(input$invert=="Yes") {
              img <- 1-img
            }
            Background.Threshold[count1] <- threshold_li(img)
            signal <- EBImage::imageData(img) > Background.Threshold[count1]
            EBImage::imageData(img) <- signal
            plot(img)
            title(paste0("Line ", count2))
          }
          shinyImageFile$Threshold <- Background.Threshold
        })
        shinyImageFile$Mean_Intensities <- matrix(0, nrow = 1, ncol = input$bands)
        shinyImageFile$Median_Intensities <- matrix(0, nrow = 1, ncol = input$bands)
        output$plot4 <- renderPlot({
          par(mfcol = c(1, input$bands))
          count1 <- 0
          Bands <- seq(1, 2*input$bands-1, by = 2)
          count2 <- 0
          for(j in Bands){
            count1 <- count1 + 1
            count2 <- count2 + 1
            img <- seg.list[[i]][[j]]
            if(colorMode(img) > 0){
              img <- 1-channel(img, input$channel)
            }
            if(input$invert=="Yes") {
              img <- 1-img
            }
            thr <- threshold_li(img, tolerance=input$tri_offset)
            signal <- EBImage::imageData(img) > thr
            EBImage::imageData(img) <- (EBImage::imageData(img) - thr)*signal
            shinyImageFile$Mean_Intensities[1,count1] <- mean(EBImage::imageData(img)[signal])
            shinyImageFile$Median_Intensities[1,count1] <- median(EBImage::imageData(img)[signal])
            plot(img)
            title(paste0("Line ", count2))
          }
        })
      }
    })
  })

  output$thresh <- renderText({
    if(!is.null(shinyImageFile$Threshold))
      paste0("Threshold(s): ", paste0(signif(shinyImageFile$Threshold, 4), collapse = ", "))
  })
  output$meanIntens <- renderText({
    if(!is.null(shinyImageFile$Threshold))
      paste0("Mean intensities: ", paste0(signif(shinyImageFile$Mean_Intensities, 4), collapse = ", "))
  })
  output$medianIntens <- renderText({
    if(!is.null(shinyImageFile$Threshold))
      paste0("Median intensities: ", paste0(signif(shinyImageFile$Median_Intensities, 4), collapse = ", "))
  })

  observe({recursiveData()})

  recursiveData <- eventReactive(input$data,{
    isolate({
      AM <- shinyImageFile$Mean_Intensities
      colnames(AM) <- paste0("Mean", 1:input$bands)
      Med <- shinyImageFile$Median_Intensities
      colnames(Med) <- paste0("Median", 1:input$bands)
      if(input$thresh == "Otsu"){
        BG.method <- matrix(c("Otsu", NA, NA), nrow = 1,
                            ncol = 3, byrow = TRUE)
        colnames(BG.method) <- c("Background", "Offset", "Probability")
      }
      if(input$thresh == "Quantile"){
        BG.method <- matrix(c("quantile", NA, input$quantile1),
                            nrow = 1, ncol = 3, byrow = TRUE)
        colnames(BG.method) <- c("Background", "Offset", "Probability")
      }
      if(input$thresh == "Triangle"){
        BG.method <- matrix(c("triangle", input$tri_offset, NA), nrow = 1,
                            ncol = 3, byrow = TRUE)
        colnames(BG.method) <- c("Background", "Offset", "Probability")
      }
      if(input$thresh == "Li"){
        BG.method <- matrix(c("Li", NA, NA), nrow = 1,
                            ncol = 3, byrow = TRUE)
        colnames(BG.method) <- c("Background", "Offset", "Probability")
      }
      seg.list <- shinyImageFile$segmentation_list
      img <- seg.list[[1]][[1]]
      if(colorMode(img) > 0){
        MODE <- input$channel
        DF <- data.frame("File" = shinyImageFile$filename,
                         "Mode" = MODE,
                         "Strip" = input$selectStrip,
                         BG.method, AM, Med,
                         check.names = FALSE)
      }else{
        DF <- data.frame("File" = shinyImageFile$filename,
                         "Mode" = NA,
                         "Strip" = input$selectStrip,
                         BG.method, AM, Med,
                         check.names = FALSE)
      }
      if(inherits(try(IntensData, silent = TRUE), "try-error"))
        IntensData <<- DF
      else
        IntensData <<- rbind(IntensData, DF)

      output$intens <- renderDT({
        DF <- IntensData
        datatable(DF)
      })
      output$plot3 <- NULL
      output$plot4 <- NULL
      output$threshPlots <- NULL
      if(!is.null(shinyImageFile$Threshold))
        shinyImageFile$Threshold <- NULL
      if(!is.null(shinyImageFile$Mean_Intensities))
        shinyImageFile$Mean_Intensities <- NULL
      if(!is.null(shinyImageFile$Median_Intensities))
        shinyImageFile$Median_Intensities <- NULL
    })
  })

  output$intens <- renderDT({
    DF <- IntensData
    datatable(DF)
  })

  observe({recursiveShowIntensData()})
  recursiveShowIntensData <- eventReactive(input$showIntensData,{
    isolate({
      updateF7Tabs(session=session, id="tabs", selected = "IntensityData")
    })
  })

  observe({recursiveDelete()})
  recursiveDelete <- eventReactive(input$deleteData,{
    isolate({
      IntensData <<- NULL
      output$intens <- renderDT({})
    })
  })

  observe({recursiveDelete2()})
  recursiveDelete2 <- eventReactive(input$deleteData2,{
    isolate({
      ExpInfo <<- NULL
      MergedData <<- NULL
      output$experiment <- renderDT({})
    })
  })

  observe({recursiveDelete3()})
  recursiveDelete3 <- eventReactive(input$deleteData3,{
    isolate({
      MergedData <<- NULL
      CalibrationData <<- NULL
      output$calibration <- renderDT({})
    })
  })

  observe({recursiveRefresh()})
  recursiveRefresh <- eventReactive(input$refreshData,{
    isolate({
      output$intens <- renderDT({
        DF <- IntensData
        datatable(DF)
      })
    })
  })

  observe({recursiveRefresh2()})
  recursiveRefresh2 <- eventReactive(input$refreshData2,{
    isolate({
      output$experiment <- renderDT({
        DF <- MergedData
        datatable(DF)
      })
    })
  })

  observe({recursiveRefresh3()})
  recursiveRefresh3 <- eventReactive(input$refreshData3,{
    isolate({
      output$calibration <- renderDT({
        DF <- CalibrationData
        datatable(DF)
      })
    })
  })

  observe({recursiveExpInfo()})

  recursiveExpInfo <- eventReactive(input$expInfo,{
    updateF7Tabs(session=session, id="tabs", selected = "ExperimentInfo")
  })

  observe({recursiveUploadIntens()})
  recursiveUploadIntens <- eventReactive(input$intensFile,{
    isolate({
      req(input$intensFile)
      tryCatch(
        DF <- read.csv(input$intensFile$datapath, header = TRUE,
                       check.names = FALSE),
        error = function(e){stop(safeError(e))}
      )
      IntensData <<- DF
      output$intens <- renderDT({
        datatable(DF)
      })
    })
  })

  observe({recursiveUploadExpFile()})
  recursiveUploadExpFile <- eventReactive(input$expFile,{
    isolate({
      req(input$expFile)
      tryCatch(
        DF <- read.csv(input$expFile$datapath, header = TRUE,
                       check.names = FALSE),
        error = function(e){stop(safeError(e))}
      )
      ExpInfo <<- DF
      MergedData <<- DF
      suppressWarnings(rm(CalibrationData, pos = 1))
      output$calibration <- renderDT({})

      output$experiment <- renderDT({
        datatable(DF)
      })
    })
  })

  observe({recursiveUploadPrepFile()})
  recursiveUploadPrepFile <- eventReactive(input$prepFile,{
    isolate({
      req(input$prepFile)
      tryCatch(
        DF <- read.csv(input$prepFile$datapath, header = TRUE,
                       check.names = FALSE),
        error = function(e){stop(safeError(e))}
      )
      CalibrationData <<- DF
      output$calibration <- renderDT({
        datatable(DF)
      })
    })
  })

  observe({recursiveMerge()})
  recursiveMerge <- eventReactive(input$merge,{
    isolate({
      DF <- merge(ExpInfo, IntensData,
                  by.x = input$mergeExp,
                  by.y = input$mergeIntens, all = TRUE)

      MergedData <<- DF
      CalibrationData <<- DF

      output$experiment <- renderDT({
        datatable(DF)
      })
    })
  })

  observe({recursivePrepare()})
  recursivePrepare <- eventReactive(input$prepare,{
    DF <- MergedData
    CalibrationData <<- DF

    output$calibration <- renderDT({
      datatable(DF)
    })

    updateF7Tabs(session=session, id="tabs", selected = "Calibration")
  })


  #Download code
  output$downloadData <- downloadHandler(
    filename = "IntensityData.csv",
    content = function(file) {
      write.csv(IntensData, file, row.names = FALSE)
    }
  )
  output$downloadData2 <- downloadHandler(
    filename = "MergedData.csv",
    content = function(file) {
      write.csv(MergedData, file, row.names = FALSE)
    }
  )
  output$downloadData3 <- downloadHandler(
    filename = "CalibrationData.csv",
    content = function(file) {
      write.csv(CalibrationData, file, row.names = FALSE)
    }
  )

  observe({recursiveCombReps()})
  recursiveCombReps <- eventReactive(input$combReps,{
    isolate({
      Cols <- c(grep("Mean", colnames(MergedData)),
                grep("Median", colnames(MergedData)))
      RES <- NULL
      if(input$colorsBands > 1){
        DF <- MergedData[,c(input$combRepsColSI, input$combRepsColCL)]
        DFuni <- DF[!duplicated(DF),]
        for (i in 1:nrow(DFuni)) {
          sel <- DF[,1] == DFuni[i,1] & DF[,2] == DFuni[i,2]
          tmp <- MergedData[sel, ]
          tmp2 <- tmp[1, ]
          if (input$radioReps == 1) #mean
            tmp2[, Cols] <- colMeans(tmp[, Cols], na.rm = TRUE)
          if (input$radioReps == 2) #median
            tmp2[, Cols] <- apply(tmp[, Cols], 2, median, na.rm = TRUE)
          RES <- rbind(RES, tmp2)
        }
      }else{
        DF <- MergedData[,input$combRepsColSI]
        for (spl in unique(MergedData[, input$combRepsColSI])) {
          tmp <- MergedData[DF == spl, ]
          tmp2 <- tmp[1, ]
          if (input$radioReps == 1) #mean
            tmp2[, Cols] <- colMeans(tmp[, Cols], na.rm = TRUE)
          if (input$radioReps == 2) #median
            tmp2[, Cols] <- apply(tmp[, Cols], 2, median, na.rm = TRUE)
          RES <- rbind(RES, tmp2)
        }
      }
      rownames(RES) <- 1:nrow(RES)
      RES <- RES[order(RES[,input$combRepsColSI]),]
      CalibrationData <<- RES

      output$calibration <- renderDT({
        datatable(RES)
      })
    })
  })

  observe({recursiveReshapeWide()})

  recursiveReshapeWide <- eventReactive(input$reshapeWide,{
    isolate({
      rm.file <- (colnames(CalibrationData) != colnames(MergedData)[1] &
                    colnames(CalibrationData) != input$reshapeCol)
      DF.split <- split(CalibrationData[,rm.file], CalibrationData[,input$reshapeCol])

      N <- length(unique(CalibrationData[,input$reshapeCol]))
      if(N > 1){
        DF <- DF.split[[1]]
        Cols <- c(grep("Mean", colnames(DF)),
                  grep("Median", colnames(DF)))
        Cols <- c(Cols, which(colnames(DF) == input$combRepsColSI))
        for(i in 2:N){
          DF <- merge(DF, DF.split[[i]][,Cols], by = input$combRepsColSI,
                      suffixes = paste0(".", names(DF.split)[c(i-1,i)]))
        }
        CalibrationData <<- DF
      }else{
        DF <- CalibrationData
      }

      output$calibration <- renderDT({
        datatable(DF)
      })
    })
  })

  MODELNUM <- 1

  output$runCali <- downloadHandler(
      filename = "Analysis Report.html",
      content = function(file) {
      output$results <- renderUI({
        f7Block(
          strong = TRUE,
          h3("Results of Calibration Analysis"),
          h4("Calibration model"),
          verbatimTextOutput("modelSummary"), br(),
          plotOutput("plot5"), br(),
          verbatimTextOutput("LOB"),
          verbatimTextOutput("LOD"),
          verbatimTextOutput("LOQ")
        )
      })

      # flush the output and plots
      output$LOB <- renderText({})
      output$LOD <- renderText({})
      output$LOQ <- renderText({})
      output$plot5 <- renderPlot({})

      concVar <- input$concVar
      respVar <- paste0("(",input$respVar,")")

      if(input$useLog){
        if(input$chosenModel == "Generalized additive model (gam)"){
          k <- ceiling(length(unique(CalibrationData[,concVar]))/2)
          FORMULA <- paste0(respVar, " ~ s(log10(", concVar, "), k = ", k, ")")
        }else{
          FORMULA <- paste0(respVar, " ~ log10(", concVar, ")")
        }
      }else{
        if(input$chosenModel == "Generalized additive model (gam)"){
          k <- ceiling(length(unique(CalibrationData[,concVar]))/2)
          FORMULA <- paste0(respVar, " ~ s(", concVar, ", k = ", k, ")")
        }else{
          FORMULA <- paste0(respVar, " ~ ", concVar)
        }
      }


      if(input$chosenModel == "Linear model (lm)" && !inherits(try(lm(as.formula(FORMULA), data=CalibrationData), silent = TRUE), "try-error")){
        modelName <- "lm"
      } else if(input$chosenModel == "Local polynomial model (loess)" && !inherits(try(loess(as.formula(FORMULA), data = CalibrationData), silent = TRUE), "try-error")){
        modelName <- "loess"
      } else if(input$chosenModel == "Generalized additive model (gam)" && !inherits(try(gam(as.formula(FORMULA), data = CalibrationData), silent = TRUE), "try-error")){
        modelName <- "gam"
      } else {
        output$results <- renderUI({
          f7Block(
            strong = TRUE,
            h3("Results of Calibration Analysis"),
            h4("Calibration model"),
            verbatimTextOutput("modelSummary")
          )
        })
        output$modelSummary <- renderPrint({print("Calibration can not be performed. Please check the formula.");
          print(paste0("Formula: ",FORMULA))})
        f7Toast(text="Error in the formula!", position="top", session=session)
        output$saveModelButton <- renderUI({})
        updateF7Tabs(session=session, id="tabs", selected = "Results")
        return(NULL)
      }

      f7Toast(text=paste("Fitting the model..."), position="top", session=session)

      SUBSET <- input$subset

      # FILENAME <<- paste0(format(Sys.time(), "%Y%m%d_%H%M%S_"), input$analysisName)

      # save(CalibrationData, FORMULA, SUBSET, PATH.OUT,
      #      file = paste0(PATH.OUT,"/", FILENAME, "_Data.RData"))
      if (input$chosenModel == "Linear model (lm)") {
        src <- normalizePath("CalibrationAnalysis(lm).Rmd")
        owd <- setwd(tempdir())
        on.exit(setwd(owd))
        file.copy(src,
                  "ReportAnalysis.Rmd", overwrite = TRUE)
      } else if (input$chosenModel == "Local polynomial model (loess)") {
        src <- normalizePath("CalibrationAnalysis(loess).Rmd")
        owd <- setwd(tempdir())
        on.exit(setwd(owd))
        file.copy(src,
                  "ReportAnalysis.Rmd", overwrite = TRUE)
      } else if (input$chosenModel == "Generalized additive model (gam)") {
        src <- normalizePath("CalibrationAnalysis(gam).Rmd")
        owd <- setwd(tempdir())
        on.exit(setwd(owd))
        file.copy(src,
                  "ReportAnalysis.Rmd", overwrite = TRUE)
      }
      out <- rmarkdown::render("ReportAnalysis.Rmd", "html_document")
      file.rename(out,file)
      # load(file = paste0(PATH.OUT, "/", FILENAME, "Results.RData")) # This line is not necessary, because the parameters are still loaded in the environment.

      predFunc <<- predFunc # make predFunc global for save model feature

      output$modelSummary <- renderPrint({ fit })

      output$plot5 <- renderPlot({
        modelPlot
      })
      output$LOB <- renderText({
        paste0("Limit of Blank (LOB): ", signif(LOB, 3))
      })
      output$LOD <- renderText({
        paste0("Limit of Detection (LOD): ", signif(LOD, 3))
      })
      output$LOQ <- renderText({
        paste0("Limit of Quantification (LOQ): ", signif(LOQ, 3))
      })

      # Adding the analysis name and model formula to the table
      modelName <- rep(modelName, nrow(CalibrationData))
      modelFormula <- rep(FORMULA, nrow(CalibrationData))
      if (input$chosenModel == "Local polynomial model (loess)") {
        modelDF <- cbind(modelName, modelFormula, fit$fitted)
      } else {
        modelDF <- cbind(modelName, modelFormula, fit$fitted.values)
      }
      colnames(modelDF) <- c(paste0(input$analysisName, ".model"),
                             paste0(input$analysisName, ".formula"),
                             paste0(input$analysisName, ".fit"))
      CalibrationData <<- cbind(CalibrationData, modelDF)
      output$calibration <- renderDT({
        datatable(CalibrationData)
      })

      MODELNUM <<- MODELNUM + 1

      output$saveModelButton <- renderUI({
        f7Block(
          strong = TRUE,
          h3("Save calibration model"),
          f7DownloadButton("saveModel", label = "Save Model")
        )
      })

      updateF7Text(session=session, inputId="analysisName", value=paste0("Model", MODELNUM))

      updateF7Tabs(session=session, id="tabs", selected = "Results")
    })

  # observe(resetFolder())
  #
  # resetFolder <- eventReactive(input$folder,{
  #   isolate({
  #     if(substring(input$folder,1,nchar(file.path(fs::path_home()))) != file.path(fs::path_home()))
  #       updateTextInput(session=session, inputId = "folder", value = file.path(fs::path_home()))
  #   })
  # })

  output$saveModel <- downloadHandler(
    filename= "Model.rds",
    content = function(file) {
      saveRDS(object=predFunc, file)
    }
  )

  # Quantification module ------------------------------------------------------
  observeEvent(input$quanData, {
    quanData <<- read.csv(input$quanData$datapath)
  })

  observeEvent(input$model, {
    calFun <<- readRDS(input$model$datapath)
  })

  observe({predictConc()})

  predictConc <- eventReactive(input$predict, {
    isolate(
      if (!is.null(calFun)) {
        if (input$quanUpload == "Upload Data" && !is.null(quanData)) {
          calConc <- calFun(quanData)
          predictData <<- cbind(quanData, calConc)
          output$quant <- renderDT({
            DF <- predictData
            datatable(DF)
          })
        } else if(input$quanUpload == "Use Intensity Data" && !is.null(IntensData)) {
          calConc <- calFun(IntensData)
          predictData <<- cbind(IntensData, calConc)
          output$quant <- renderDT({
            DF <- predictData
            datatable(DF)
          })
        } else {
          output$quant <- renderDT({})
        }
      }
    )
  })

  #allows user to download prediction
  output$downloadData4 <- downloadHandler(
    filename = "PredictData.csv",
    content = function(file) {
      write.csv(predictData, file, row.names = FALSE)
    }
  )
}
shinyApp(ui = ui, server = server)
fpaskali/LFApp documentation built on May 31, 2024, 10:14 a.m.