library(shiny) library(shinydashboard) library(shinyjs) # Amino Acid Data amino_acid_data <- data.frame( code = c("A", "R", "N", "D", "C", "E", "Q", "G", "H", "I", "L", "K", "M", "F", "P", "S", "T", "W", "Y", "V"), name = c("Alanine", "Arginine", "Asparagine", "Aspartic acid", "Cysteine", "Glutamic acid", "Glutamine", "Glycine", "Histidine", "Isoleucine", "Leucine", "Lysine", "Methionine", "Phenylalanine", "Proline", "Serine", "Threonine", "Tryptophan", "Tyrosine", "Valine"), weight = c(89.1, 174.2, 132.1, 133.1, 121.2, 147.1, 146.2, 75.1, 155.2, 131.2, 131.2, 146.2, 149.2, 165.2, 115.1, 105.1, 119.1, 204.2, 181.2, 117.1), extinction280 = c(0, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5500, 1490, 0) ) # UI ui <- dashboardPage( dashboardHeader(title = "Protein Concentration Calculator"), dashboardSidebar( sidebarMenu( menuItem("Absorbance to Concentration", tabName = "absorbance"), menuItem("Weight to Mole", tabName = "weightToMole"), menuItem("Mole to Weight", tabName = "moleToWeight"), menuItem("Molar Concentration", tabName = "molarConcentration"), menuItem("Amino Acid Calculator", tabName = "aminoAcid"), menuItem("Extinction Calculator", tabName = "extinction") ) ), dashboardBody( useShinyjs(), tabItems( # Absorbance to Concentration Tab tabItem(tabName = "absorbance", fluidRow( box(width = 12, title = "Absorbance to Concentration", numericInput("absorbance", "Absorbance (A)", value = NULL, min = 0), numericInput("extinctionCoefficient", "Extinction Coefficient (M⁻¹cm⁻¹)", value = NULL, min = 0), numericInput("pathLength", "Path Length (cm)", value = 1.0, min = 0), actionButton("calculateConcentration", "Calculate Concentration", class = "btn-primary"), hr(), conditionalPanel( condition = "output.concentration !== null", h4("Result:"), verbatimTextOutput("concentration"), h4("Step-by-Step:"), verbatimTextOutput("concentrationSteps") ) ) ) ), # Weight to Mole Tab tabItem(tabName = "weightToMole", fluidRow( box(width = 12, title = "Weight to Mole", numericInput("weight", "Weight (g)", value = NULL, min = 0), numericInput("molarWeightWM", "Molar Weight (g/mol)", value = NULL, min = 0), actionButton("calculateMole", "Calculate Mole", class = "btn-primary"), hr(), conditionalPanel( condition = "output.weightToMoleResult !== null", h4("Result:"), verbatimTextOutput("weightToMoleResult"), h4("Step-by-Step:"), verbatimTextOutput("weightToMoleSteps") ) ) ) ), # Mole to Weight Tab tabItem(tabName = "moleToWeight", fluidRow( box(width = 12, title = "Mole to Weight", numericInput("mole", "Mole", value = NULL, min = 0), numericInput("molarWeightMW", "Molar Weight (g/mol)", value = NULL, min = 0), actionButton("calculateWeight", "Calculate Weight", class = "btn-primary"), hr(), conditionalPanel( condition = "output.moleToWeightResult !== null", h4("Result:"), verbatimTextOutput("moleToWeightResult"), h4("Step-by-Step:"), verbatimTextOutput("moleToWeightSteps") ) ) ) ), # Molar Concentration Tab tabItem(tabName = "molarConcentration", fluidRow( box(width = 12, title = "Molar Concentration", numericInput("moleMC", "Mole", value = NULL, min = 0), numericInput("volume", "Volume (L)", value = NULL, min = 0), actionButton("calculateMolarConcentration", "Calculate Molar Concentration", class = "btn-primary"), hr(), conditionalPanel( condition = "output.molarConcentration !== null", h4("Result:"), verbatimTextOutput("molarConcentration"), h4("Step-by-Step:"), verbatimTextOutput("molarConcentrationSteps") ) ) ) ), # Amino Acid Calculator Tab tabItem(tabName = "aminoAcid", fluidRow( box(width = 12, title = "Amino Acid to Molecular Weight", textAreaInput("aminoAcidSequence", "Protein Sequence", "", height = "150px"), actionButton("calculateMolecularWeight", "Calculate Molecular Weight", class = "btn-primary"), hr(), conditionalPanel( condition = "output.molecularWeight !== null", h4("Result:"), verbatimTextOutput("molecularWeight"), h4("Step-by-Step:"), verbatimTextOutput("molecularWeightSteps") ) ) ) ), # Extinction Coefficient Calculator Tab tabItem(tabName = "extinction", fluidRow( box(width = 12, title = "Extinction Coefficient Calculator", textAreaInput("extinctionSequence", "Protein Sequence", "", height = "150px"), actionButton("calculateExtinctionCoefficient", "Calculate Extinction Coefficient", class = "btn-primary"), hr(), conditionalPanel( condition = "output.extinctionResult !== null", h4("Result:"), verbatimTextOutput("extinctionResult"), h4("Step-by-Step:"), verbatimTextOutput("extinctionSteps") ) ) ) ) ), # Reset Button at the bottom of each page fluidRow( column(12, align = "center", actionButton("resetAll", "Reset All Fields", style = "background-color: #3c8dbc; color: white; margin-top: 20px; margin-bottom: 20px;") ) ) ) ) # Server server <- function(input, output, session) { # Reset function for all inputs observeEvent(input$resetAll, { # Reset absorbance tab updateNumericInput(session, "absorbance", value = NULL) updateNumericInput(session, "extinctionCoefficient", value = NULL) updateNumericInput(session, "pathLength", value = 1.0) # Reset weight to mole tab updateNumericInput(session, "weight", value = NULL) updateNumericInput(session, "molarWeightWM", value = NULL) # Reset mole to weight tab updateNumericInput(session, "mole", value = NULL) updateNumericInput(session, "molarWeightMW", value = NULL) # Reset molar concentration tab updateNumericInput(session, "moleMC", value = NULL) updateNumericInput(session, "volume", value = NULL) # Reset amino acid calculator tab updateTextAreaInput(session, "aminoAcidSequence", value = "") # Reset extinction coefficient calculator tab updateTextAreaInput(session, "extinctionSequence", value = "") # Clear all outputs output$concentration <- renderText("") output$concentrationSteps <- renderText("") output$weightToMoleResult <- renderText("") output$weightToMoleSteps <- renderText("") output$moleToWeightResult <- renderText("") output$moleToWeightSteps <- renderText("") output$molarConcentration <- renderText("") output$molarConcentrationSteps <- renderText("") output$molecularWeight <- renderText("") output$molecularWeightSteps <- renderText("") output$extinctionResult <- renderText("") output$extinctionSteps <- renderText("") }) # Calculate Concentration observeEvent(input$calculateConcentration, { req(input$absorbance, input$extinctionCoefficient, input$pathLength) if(input$extinctionCoefficient <= 0 || input$pathLength <= 0) { output$concentration <- renderText("Invalid input") output$concentrationSteps <- renderText("Ensure extinction coefficient and path length are greater than zero.") return() } result <- input$absorbance / (input$extinctionCoefficient * input$pathLength) output$concentration <- renderText(format(result, scientific = TRUE, digits = 6)) output$concentrationSteps <- renderText(paste( "Step-by-Step:", "1. Concentration = Absorbance ÷ (Extinction Coefficient × Path Length)", paste("2.", input$absorbance, "÷ (", input$extinctionCoefficient, "×", input$pathLength, ") =", result), sep = "\n" )) }) # Calculate Weight to Mole observeEvent(input$calculateMole, { req(input$weight, input$molarWeightWM) if(input$molarWeightWM <= 0) { output$weightToMoleResult <- renderText("Invalid input") output$weightToMoleSteps <- renderText("Ensure molar weight is greater than zero.") return() } result <- input$weight / input$molarWeightWM output$weightToMoleResult <- renderText(format(result, scientific = TRUE, digits = 6)) output$weightToMoleSteps <- renderText(paste( "Step-by-Step:", "1. Mole = Weight ÷ Molar Weight", paste("2.", input$weight, "÷", input$molarWeightWM, "=", result), sep = "\n" )) }) # Calculate Mole to Weight observeEvent(input$calculateWeight, { req(input$mole, input$molarWeightMW) if(input$molarWeightMW <= 0) { output$moleToWeightResult <- renderText("Invalid input") output$moleToWeightSteps <- renderText("Ensure molar weight is greater than zero.") return() } result <- input$mole * input$molarWeightMW output$moleToWeightResult <- renderText(format(result, scientific = TRUE, digits = 6)) output$moleToWeightSteps <- renderText(paste( "Step-by-Step:", "1. Weight = Mole × Molar Weight", paste("2.", input$mole, "×", input$molarWeightMW, "=", result), sep = "\n" )) }) # Calculate Molar Concentration observeEvent(input$calculateMolarConcentration, { req(input$moleMC, input$volume) if(input$volume <= 0) { output$molarConcentration <- renderText("Invalid input") output$molarConcentrationSteps <- renderText("Ensure volume is greater than zero.") return() } result <- input$moleMC / input$volume output$molarConcentration <- renderText(format(result, scientific = TRUE, digits = 6)) output$molarConcentrationSteps <- renderText(paste( "Step-by-Step:", "1. Molar Concentration = Mole ÷ Volume", paste("2.", input$moleMC, "÷", input$volume, "=", result), sep = "\n" )) }) # Calculate Molecular Weight from Amino Acid Sequence observeEvent(input$calculateMolecularWeight, { req(input$aminoAcidSequence) # Clean sequence - keep only letters and convert to uppercase cleanSequence <- toupper(gsub("[^A-Za-z]", "", input$aminoAcidSequence)) if(nchar(cleanSequence) == 0) { output$molecularWeight <- renderText("Invalid input") output$molecularWeightSteps <- renderText("Please enter a valid amino acid sequence.") return() } # Split sequence into individual amino acids aaVector <- strsplit(cleanSequence, "")[[1]] # Count amino acids aaCounts <- table(aaVector) # Initialize variables for calculation totalWeight <- 0 stepsText <- "Step-by-Step:\nAmino acid composition:\n" # Calculate weight for each amino acid for(aa in names(aaCounts)) { aaData <- amino_acid_data[amino_acid_data$code == aa, ] # Check if amino acid code is valid if(nrow(aaData) == 0) { output$molecularWeight <- renderText(paste("Unknown amino acid code:", aa)) output$molecularWeightSteps <- renderText("Please check your sequence for valid amino acid codes.") return() } count <- aaCounts[aa] weight <- aaData$weight * count totalWeight <- totalWeight + weight stepsText <- paste0(stepsText, aaData$name, " (", aa, "): ", count, " × ", format(aaData$weight, nsmall = 1), " = ", format(weight, nsmall = 1), " Da\n") } # Account for water loss in peptide bonds waterMass <- 18.015 peptideBonds <- nchar(cleanSequence) - 1 waterLoss <- waterMass * peptideBonds totalWeight <- totalWeight - waterLoss stepsText <- paste0(stepsText, "\nWater loss from peptide bonds:\n", "(", peptideBonds, " bonds × ", format(waterMass, nsmall = 3), " Da) = ", format(waterLoss, nsmall = 1), " Da\n", "\nFinal molecular weight: ", format(totalWeight, nsmall = 1), " Da") output$molecularWeight <- renderText(paste0(format(totalWeight, nsmall = 1), " Da")) output$molecularWeightSteps <- renderText(stepsText) }) # Calculate Extinction Coefficient observeEvent(input$calculateExtinctionCoefficient, { req(input$extinctionSequence) # Clean sequence - keep only letters and convert to uppercase cleanSequence <- toupper(gsub("[^A-Za-z]", "", input$extinctionSequence)) if(nchar(cleanSequence) == 0) { output$extinctionResult <- renderText("Invalid input") output$extinctionSteps <- renderText("Please enter a valid amino acid sequence.") return() } # Split sequence into individual amino acids aaVector <- strsplit(cleanSequence, "")[[1]] # Count amino acids aaCounts <- table(aaVector) # Initialize variables for calculation totalExtinction <- 0 stepsText <- "Step-by-Step:\nContributing amino acids:\n" # Only Trp, Tyr, and Cys contribute to extinction coefficient at 280nm contributingAAs <- c("W", "Y", "C") # Calculate extinction for each contributing amino acid for(aa in contributingAAs) { if(aa %in% names(aaCounts)) { count <- aaCounts[aa] aaData <- amino_acid_data[amino_acid_data$code == aa, ] contribution <- aaData$extinction280 * count totalExtinction <- totalExtinction + contribution if(count > 0) { stepsText <- paste0(stepsText, aaData$name, " (", aa, "): ", count, " × ", format(aaData$extinction280, nsmall = 0), " = ", format(contribution, nsmall = 0), " M⁻¹cm⁻¹\n") } } } stepsText <- paste0(stepsText, "\nTotal extinction coefficient: ", format(totalExtinction, nsmall = 0), " M⁻¹cm⁻¹") output$extinctionResult <- renderText(paste0(format(totalExtinction, nsmall = 0), " M⁻¹cm⁻¹")) output$extinctionSteps <- renderText(stepsText) }) } # Run the application shinyApp(ui = ui, server = server)