# Load required libraries library(shiny) library(readxl) library(tidyverse) library(stringr) library(openxlsx) # Define the timeout period in seconds timeoutSeconds <- 1200 # Create the JavaScript code to handle user inactivity inactivity <- sprintf(" function idleTimer() { var t; var timeout = %s * 1000; // Convert seconds to milliseconds // Events that reset the timer var events = ['mousemove', 'mousedown', 'click', 'scroll', 'keypress']; // Function to log out and notify the user function logout() { alert('You have been disconnected due to inactivity. Refresh your browser to use the webapp again. - Dr. Eduardo'); Shiny.setInputValue('timeOut', '%s'); } // Function to reset the timer function resetTimer() { clearTimeout(t); t = setTimeout(logout, timeout); } // Attach resetTimer to each event events.forEach(function(event) { window.addEventListener(event, resetTimer); }); // Initialize the timer t = setTimeout(logout, timeout); } // Initialize the idle timer when the document is ready document.addEventListener('DOMContentLoaded', function() { idleTimer(); }); ", timeoutSeconds, timeoutSeconds) ##### read_and_preprocess <- function(path, filename) { df <- read_excel(path) # Remove columns with entirely NA entries df <- df[, colSums(is.na(df)) != nrow(df), drop = FALSE] columns_selected <- c("ID number", "Last name", "Time taken", "State", "Grade/5.00") new_column_names <- c("ID_Number", "Last_Name", "Time_Taken", "State", "Grade") # Select and rename columns df <- df[, columns_selected, drop = FALSE] colnames(df) <- new_column_names # Convert Grade to numeric and Last_Name to title case df$Grade <- as.numeric(df$Grade) df$Last_Name <- str_to_title(df$Last_Name) # Remove rows with missing values df <- df[complete.cases(df), ] return(df) } # Function to calculate additional columns calculate_additional_columns <- function(df, attempted, filename) { avg_col_name <- paste("Average", filename, sep = "_") df <- df %>% group_by(ID_Number) %>% mutate(nAttempt = n(), !!avg_col_name := ifelse(nAttempt >= attempted, round(mean(sort(Grade, decreasing = TRUE)[1:attempted]),0), round(sum(Grade) / attempted))) %>% distinct(ID_Number, .keep_all = TRUE) return(df) } ##### # Define UI ui <- fluidPage( #tags$script(HTML(inactivity)), titlePanel( h4("UTAS - Online Marks Aggregator"), windowTitle = "Marks Aggregator" ), sidebarLayout( sidebarPanel( numericInput( "attempt", "Min Number of Attempts", value = 1, min = 1, max = 10, step = 1 ), fileInput( "files", "Upload Excel file(s)", accept = c(".xlsx"), multiple = TRUE, placeholder = "Please select excel file/s" ), helpText("Upload only Excel files with .XLSX extension to ensure consistency."), br(), downloadButton("downloadData", "Download"), hr(), div( class = "fluent-footer", div( class = "fluent-footer-legal", div( class = "fluent-footer-legal-left", a("Privacy Policy", href = "#"), " | ", a("Terms of Use", href = "#") ), div( class = "fluent-footer-legal-right", "© 2023 College of Computing and Information Sciences" ) ) ), tags$footer( style = "text-align: left; padding-top: 10px; font-size: 12px;", "Built by ", tags$a(href = "https://www.google.com/search?q=dr.+eduardo+lacap+jr&sca_esv=4136c3af1df3852c&sca_upv=1&sxsrf=ADLYWIIeq7fb1cWKSiyieFVwGRe7qJ40NA%3A1716361100562&ei=jJdNZqHvIZbeseMPj4O8sAs&oq=dr.+eduardo+lacap&gs_lp=Egxnd3Mtd2l6LXNlcnAiEWRyLiBlZHVhcmRvIGxhY2FwKgIIATIHECMYsAMYJzIHECMYsAMYJzILEAAYgAQYsAMYogQyCxAAGIAEGLADGKIEMgsQABiwAxiiBBiJBTILEAAYgAQYsAMYogRIuA1QAFgAcAF4AJABAJgBAKABAKoBALgBAcgBAJgCAaACBJgDAIgGAZAGBpIHATGgBwA&sclient=gws-wiz-serp", "Dr. Eduardo M. Lacap, Jr.") ) ), mainPanel( tableOutput("table") ) ) ) # Define server logic server <- function(input, output, session) { # Reactive expression for the number of attempts attempted <- reactive({ input$attempt }) # Reactive expression for the nature of the files uploaded independnt <- reactive({input$independent}) # Reactive expression to read and combine uploaded Excel files combined_df <- reactive({ req(input$files$datapath) # Ensure files are uploaded df_list <- mapply(function(path, name) { df <- read_and_preprocess(path, tools::file_path_sans_ext(basename(name))) df <- calculate_additional_columns(df, attempted(), tools::file_path_sans_ext(basename(name))) return(df) }, input$files$datapath, input$files$name, SIMPLIFY = FALSE) combined_df <- bind_rows(df_list) combined_df <- if (length(df_list) == 1) { df_list[[1]] %>% select(ID_Number, Last_Name, starts_with("Average")) } else { as.data.frame(combined_df) %>% select(ID_Number, Last_Name, starts_with("Average")) %>% pivot_longer(cols = starts_with("Average_"), names_to = "Average_Type", values_to = "Average_Value") %>% filter(!is.na(Average_Value)) %>% pivot_wider(names_from = "Average_Type", values_from = "Average_Value") %>% mutate_at(., vars(starts_with("Average_")), ~replace(., is.na(.), 0), values_fn = list) %>% mutate(Marks = round(rowMeans(select(., starts_with("Average_")), na.rm = TRUE)+0.1,0)) } return(combined_df) }) # Display combined data as a table output$table <- renderTable({ combined_df() }) # Download combined data output$downloadData <- downloadHandler( filename = function() { paste("MarksAggregated_", attempted(), "_MinAttempt.xlsx", sep = "") }, content = function(file) { write.xlsx(combined_df(), file) } ) } # Run the application shinyApp(ui = ui, server = server)