library(shiny) library(shinydashboard) library(shinyWidgets) library(dplyr) # Define the Enzyme data structure enzyme_data <- list( list( name = "T4 DNA Ligase", brand = "NEB", activityUnits = "U", concentrationUnitsPerML = 400000, # NEB T4 DNA Ligase: 400,000 U/mL optimalTemperature_lower = 16, optimalTemperature_upper = 25, incubationTimeRange_lower = 30, incubationTimeRange_upper = 1440, # Overnight ligation included storageTemperature_lower = -25, storageTemperature_upper = -15, notes = "Best for sticky-end ligation at 16°C overnight." ), list( name = "Taq DNA Polymerase", brand = "NEB", activityUnits = "U", concentrationUnitsPerML = 5000, optimalTemperature_lower = 72, optimalTemperature_upper = 80, incubationTimeRange_lower = 1, incubationTimeRange_upper = 3, storageTemperature_lower = -25, storageTemperature_upper = -15, notes = "1 U incorporates 10 nmol of dNTP in 30 minutes." ), list( name = "Phusion DNA Polymerase", brand = "NEB", activityUnits = "U", concentrationUnitsPerML = 2000, optimalTemperature_lower = 72, optimalTemperature_upper = 75, incubationTimeRange_lower = 0.5, incubationTimeRange_upper = 1.5, storageTemperature_lower = -25, storageTemperature_upper = -15, notes = "High-fidelity PCR. 1 U incorporates 10 nmol of dNTP in 30 min." ), list( name = "EcoRI", brand = "Promega", activityUnits = "U", concentrationUnitsPerML = 10000, optimalTemperature_lower = 37, optimalTemperature_upper = 37, incubationTimeRange_lower = 15, incubationTimeRange_upper = 60, storageTemperature_lower = -25, storageTemperature_upper = -15, notes = "1 U digests 1 μg of DNA in 1 hour." ), list( name = "Proteinase K", brand = "Sigma", activityUnits = "U", concentrationUnitsPerML = 20000, optimalTemperature_lower = 37, optimalTemperature_upper = 60, incubationTimeRange_lower = 10, incubationTimeRange_upper = 180, storageTemperature_lower = -25, storageTemperature_upper = 4, notes = "1 U degrades 1 µg protein in 10 min. Use 10x for full digestion." ), list( name = "SapI", brand = "NEB", activityUnits = "U", concentrationUnitsPerML = 10000, # NEB SapI standard concentration optimalTemperature_lower = 37, optimalTemperature_upper = 37, incubationTimeRange_lower = 15, incubationTimeRange_upper = 60, storageTemperature_lower = -25, storageTemperature_upper = -15, notes = "Type IIS restriction enzyme. 1 U digests 1 μg of DNA in 1 hour." ) ) # Convert enzyme data to a data frame for easier manipulation enzymes <- do.call(rbind, lapply(enzyme_data, function(x) { data.frame( name = x$name, brand = x$brand, activityUnits = x$activityUnits, concentrationUnitsPerML = x$concentrationUnitsPerML, optimalTemperature_lower = x$optimalTemperature_lower, optimalTemperature_upper = x$optimalTemperature_upper, incubationTimeRange_lower = x$incubationTimeRange_lower, incubationTimeRange_upper = x$incubationTimeRange_upper, storageTemperature_lower = x$storageTemperature_lower, storageTemperature_upper = x$storageTemperature_upper, notes = x$notes, stringsAsFactors = FALSE ) })) # Create enzyme display names for the dropdown enzyme_choices <- paste0(enzymes$name, " (", enzymes$brand, ")") names(enzyme_choices) <- enzyme_choices # Define UI ui <- dashboardPage( dashboardHeader(title = "Enzyme Calculator"), dashboardSidebar( sidebarMenu( menuItem("Calculator", tabName = "calculator", icon = icon("calculator")), menuItem("About", tabName = "about", icon = icon("info-circle")) ) ), dashboardBody( tabItems( # Calculator tab tabItem(tabName = "calculator", fluidRow( box( title = "Enzyme Selection", status = "primary", solidHeader = TRUE, selectInput("enzymeSelect", "Select Enzyme", choices = c("Select an enzyme" = "", enzyme_choices)), actionButton("viewDetails", "View Enzyme Details", icon = icon("search-plus")) ), box( title = "Reaction Parameters", status = "primary", solidHeader = TRUE, numericInput("reactionVolume", "Reaction Volume (μL)", 50, min = 0), uiOutput("dnaAmountUI"), uiOutput("temperatureUI") ) ), fluidRow( box( width = 12, actionButton("calculate", "Calculate", icon = icon("vial"), style = "color: #fff; background-color: #3c8dbc"), uiOutput("resultsUI") ) ) ), # About tab tabItem(tabName = "about", box( title = "About Enzyme Calculator", status = "info", solidHeader = TRUE, width = 12, p("This application helps calculate the optimal enzyme volumes for laboratory reactions."), p("Select an enzyme, input your reaction parameters, and the calculator will determine the appropriate enzyme volume for your experiment."), p("Enzyme data is based on manufacturer specifications and common laboratory protocols.") ) ) ) ) ) # Define server logic server <- function(input, output, session) { # Reactive value to store the currently selected enzyme selectedEnzyme <- reactive({ req(input$enzymeSelect) idx <- match(input$enzymeSelect, enzyme_choices) if (!is.na(idx)) { return(enzymes[idx, ]) } else { return(NULL) } }) # Dynamic UI for DNA amount (changes label based on enzyme) output$dnaAmountUI <- renderUI({ enzyme <- selectedEnzyme() if (is.null(enzyme)) { return(numericInput("dnaAmount", "DNA Amount", 1250, min = 0)) } label <- if (enzyme$name == "EcoRI" || enzyme$name == "SapI") { "DNA (µg)" } else { "DNA (pmol)" } numericInput("dnaAmount", label, 1250, min = 0) }) # Dynamic UI for temperature slider output$temperatureUI <- renderUI({ enzyme <- selectedEnzyme() if (is.null(enzyme)) { return(p("Select an enzyme to adjust temperature")) } lower <- enzyme$optimalTemperature_lower upper <- enzyme$optimalTemperature_upper if (lower < upper) { sliderInput("reactionTemp", "Temperature (°C)", min = lower, max = upper, value = (lower + upper) / 2, step = 1) } else { # Fixed temperature p(paste0("Temperature: ", lower, "°C (Fixed)")) } }) # Modal dialog for enzyme details observeEvent(input$viewDetails, { enzyme <- selectedEnzyme() if (!is.null(enzyme)) { showModal(modalDialog( title = paste0(enzyme$name, " Details"), h4("General Information"), tags$table(class = "table", tags$tr( tags$td("Name"), tags$td(tags$strong(enzyme$name)) ), tags$tr( tags$td("Brand"), tags$td(enzyme$brand) ), tags$tr( tags$td("Concentration"), tags$td(paste0(enzyme$concentrationUnitsPerML, " ", enzyme$activityUnits, "/mL")) ) ), h4("Optimal Conditions"), tags$table(class = "table", tags$tr( tags$td("Temperature Range"), tags$td(paste0(enzyme$optimalTemperature_lower, "-", enzyme$optimalTemperature_upper, "°C")) ), tags$tr( tags$td("Incubation Time"), tags$td(paste0(enzyme$incubationTimeRange_lower, "-", enzyme$incubationTimeRange_upper, " min")) ), tags$tr( tags$td("Storage"), tags$td(paste0(enzyme$storageTemperature_lower, "°C to ", enzyme$storageTemperature_upper, "°C")) ) ), h4("Notes"), p(enzyme$notes), easyClose = TRUE, footer = modalButton("Close") )) } }) # Calculate enzyme volume when button is clicked calculatedVolume <- eventReactive(input$calculate, { enzyme <- selectedEnzyme() req(enzyme) req(input$reactionVolume) req(input$dnaAmount) volume <- input$reactionVolume dnaInput <- input$dnaAmount requiredUnits <- 0 # Calculate required units based on enzyme and DNA amount if (enzyme$name == "T4 DNA Ligase") { minWeissUnits <- dnaInput * 2 * 0.01 maxWeissUnits <- dnaInput * 2 * 0.05 requiredUnits <- (minWeissUnits + maxWeissUnits) / 2 * 20 # Convert Weiss → U } else if (enzyme$name == "Taq DNA Polymerase") { requiredUnits <- max(1, dnaInput / 10) # 1 U per 10 pmol DNA } else if (enzyme$name == "Phusion DNA Polymerase") { requiredUnits <- max(1, dnaInput / 20) # High-fidelity, 1 U per 20 pmol DNA } else if (enzyme$name == "EcoRI" || enzyme$name == "SapI") { requiredUnits <- max(1, dnaInput) # 1 U per µg DNA } else if (enzyme$name == "Proteinase K") { requiredUnits <- dnaInput * 10 # Assume 10x excess for digestion } # Convert required units to enzyme volume volumeResult <- requiredUnits / enzyme$concentrationUnitsPerML * 1000 # Ensure volume does not exceed 10% of total reaction volume if (volumeResult > volume * 0.1) { volumeResult <- volume * 0.1 } return(volumeResult) }) # Display results when calculation is performed output$resultsUI <- renderUI({ req(calculatedVolume()) enzyme <- selectedEnzyme() req(enzyme) volume <- calculatedVolume() box( title = "Results", status = "success", solidHeader = TRUE, width = 12, tags$table(class = "table", tags$tr( tags$td("Required Volume:"), tags$td(tags$strong(paste0(format(round(volume, 2), nsmall = 2), " μL"))) ) ), tags$div( class = "alert alert-info", tags$p(paste0("Add ", format(round(volume, 2), nsmall = 2), " μL of ", enzyme$name, " to your ", input$reactionVolume, " μL reaction.")), tags$p(paste0("Incubate at ", enzyme$optimalTemperature_lower, "-", enzyme$optimalTemperature_upper, "°C for ", enzyme$incubationTimeRange_lower, "-", enzyme$incubationTimeRange_upper, " minutes.")), tags$p(paste0("Store the enzyme at ", enzyme$storageTemperature_lower, "°C to ", enzyme$storageTemperature_upper, "°C when not in use.")) ) ) }) } # Run the application shinyApp(ui = ui, server = server)