External Data storage in Shiny apps - r

I am developing a shiny application which save the data entered on the user interface. I have refered the url on shiny rstudio page so by using this page, the code i have written is as mentioned below:
outputDir <- "C:\\Users/dell/Desktop/"
saveData <- function(data) {
data <- t(data)
fileName <- sprintf("%s_%s.csv", as.integer(Sys.time()), digest::digest(data))
write.csv(
x = data, sep = ",",
file = file.path(outputDir, fileName),
row.names = FALSE, quote = TRUE
)
}
loadData <- function() {
files <- list.files(outputDir, full.names = TRUE)
data <- lapply(files, read.csv, stringsAsFactors = FALSE)
data <- do.call(rbind, data)
data
}
library(shiny)
fields <- c("name", "staff_name")
shinyApp(
ui = fluidPage(
titlePanel("attendance System"),
DT::dataTableOutput("responses", width = 300), tags$hr(),
textInput("name", "Accession Number", ""),
selectInput("staff_name", "Staff Name",
c("Rajiv" = "RT",
"Arvind " = "AKS",
"Ashutosh " = "AS")),
actionButton("submit", "Submit")
),
server = function(input, output, session) {
formData <- reactive({
data <- sapply(fields, function(x) input[[x]])
data
})
observeEvent(input$submit, {
saveData(formData())
})
output$responses <- DT::renderDataTable({
input$submit
loadData()
})
}
)
The above code create a new file for each entry. I am looking for a single file in which all entry to be added.

This will give you a unique file name based on time of save and content of the file:
fileName <- sprintf("%s_%s.csv", as.integer(Sys.time()), digest::digest(data))
You can give it a single name like:
fileName <- 'input_bu.csv'
Like #ismirsehregal, I'd recommend bookmarking for this though.

after looking various solutions. I reached at below code to save the data in a single file as it is entered.
library(shiny)
outputDir <- "C:\\Users/dell/Desktop/"
saveData <- function(data) {
data <- as.data.frame(t(data))
if (exists("responsesiq")) {
responsesiq <<- rbind(responsesiq, data)
} else {
responsesiq <<- data
}
fileName <- "test_igntu.csv"
write.csv(
x = responsesiq, sep = ",",
file = file.path(outputDir, fileName),
row.names = FALSE, quote = TRUE
)
}
fields <- c("acc", "staff_name")
shinyApp(
ui = fluidPage(
titlePanel("Attendance System"),
DT::dataTableOutput("responsesiq", width = 300), tags$hr(),
numericInput("acc", "AccNumber", ""),
selectInput("staff_name", "Staff Name",
c("Rajiv" = "RT",
"Arvind" = "AKS",
"Ashutosh" = "AS")),
actionButton("submit", "Submit")
),
server = function(input, output, session) {
# Whenever a field is filled, aggregate all form data
formData <- reactive({
data <- sapply(fields, function(x) input[[x]])
data
})
# When the Submit button is clicked, save the form data
observeEvent(input$submit, {
saveData(formData())
})
}
)

Related

Having Trouble Getting Download Handler in Shiny to Work

I am trying to build a shiny platform that can take in an input file, paste the output into a table on display, and then have a download button to download the results of the file back to your computer. I've tried numerous different ways, and it hasn't worked.
UI Code:
tabItem(tabName = "home",
h2("Cancer Publications Compiler"),
sidebarLayout(position = "left",
# File Upload
sidebarPanel(
radioButtons("data_t", "Select the Data Type Here",
c(Excel = ",", "Text = "\t")),
fileInput("data_f", "Upload Your Data Here")),
# Download Input
mainPanel(width = 8,align = "center",
div(style = "border:1px black solid;width:90%;font-size:10px;",tableOutput("out_chart")),
downloadButton("downloadData", "Download")))
),
Server:
fileext = reactive({
switch(input$data_t,
"Excel" = "csv", "Text" = "txt")
})
## Create Output for file selector ##
data_file <- reactive({
if(is.null(input$data_f)){return()}
else{
file_spec <- input$data_f
aa <- read.table(file_spec$datapath, header = TRUE, sep = input$data_t)
return(aa)
}
})
# Connects the input and output to launch the data table
## Create Output for table from tool ##
output$out_chart <- renderTable({
if(is.null(input$data_f)){return()}
else {
data_file()
}
})
output$donwloadData <- downloadHandler(
filename = function(){
paste("data-", fileext(), sep = ".")
},
content = function(file){
sep <- switch(input$data_t, "Excel" = ",", "Text" = "\t")
write.table(data_file(), file, sep = sep,
row.names = FALSE)
})
Can anyone help me with a fix for this problem so that the download handler will work how I want it to?
UPDATE: I have edited the code and have updated it on here. My problem now is that when I click download to download the outputted table, I am getting a .htm download of a very low rendered and weird looking version of my webpage.
You had some typos, and other issues. Try this
ui <- fluidPage(
tabItem(tabName = "home",
h2("Cancer Publications Compiler"),
sidebarLayout(position = "left",
# File Upload
sidebarPanel(
radioButtons("data_t", "Select the Data Type Here", c("Excel" = "csv", "Text" = "txt")) ,
fileInput("data_f", "Upload Your Data Here")
),
# Download Input
mainPanel(width = 8,align = "center",
div(style = "border:1px black solid;width:90%;font-size:10px;",tableOutput("out_chart")),
downloadButton("downloadData", "Download"))
)
)
)
server<- function (input, output, session) {
sep <- reactive({
req(input$data_t)
switch(input$data_t,
"csv" = ",", "txt" = "\t")
})
## Create Output for file selector ##
data_file <- reactive({
if(is.null(input$data_f)){return()}
else{
file_spec <- input$data_f
aa <- read.table(file_spec$datapath, header = TRUE, sep = sep())
return(aa)
}
})
# Connects the input and output to launch the data table
## Create Output for table from tool ##
output$out_chart <- renderTable({
print(input$data_t)
if(is.null(input$data_f)){return()}
else {
data_file()
}
})
output$downloadData <- downloadHandler(
filename = function(){
paste("data-", input$data_t, sep = ".")
},
content = function(file){
write.table(data_file(), file, sep = sep(), row.names = FALSE)
}
)
}
shinyApp(ui = ui, server = server)

How to conditionally download a plot?

The below reproducible code allows the user to select either a data table or a plot of the data for viewing (via input$view). I'm trying to create a conditional around the downloadHandler() so that if the user is viewing the data table and chooses to download, then the data is downloaded; otherwise if the user is viewing the plot and chooses to download then a plot in PNG format is downloaded. I'm running into issues around input$view reactivity. How would I modify the code below to conditionally download whichever (data or plot) the user is viewing?
The code as posted below works for viewing either data or plot, but only allows the data table to be downloaded. Offending lines of code that otherwise cause a crash are commented out.
Reproducible code:
library(shiny)
library(ggplot2)
ui <- fluidPage(
radioButtons("view",
label = "View data or plot",
choiceNames = c('Data','Plot'),
choiceValues = c('viewData','viewPlot'),
selected = 'viewData',
inline = TRUE
),
conditionalPanel("input.view == 'viewData'",tableOutput("DF")),
conditionalPanel("input.view == 'viewPlot'",plotOutput("plotDF")),
downloadButton("download","Download",style = "width:20%;")
)
server <- function(input, output, session) {
data <- data.frame(Period = c(1,2,3,4,5,6),Value = c(10,20,15,40,35,30))
data1 <- reactiveValues()
inputView <- reactive(input$view) # attempt to make input$view reactive
observeEvent(input$view,{data1$plot <- ggplot(data, aes(Period,Value)) + geom_line()})
output$DF <- renderTable(data)
output$plotDF <- renderPlot(data1$plot)
output$download <-
# if(inputView() == 'viewData'){
downloadHandler(
filename = function()
paste("dataDownload","csv",sep="."),
content = function(file){
write.table(
data,
na = "",
file,
sep = ",",
col.names = TRUE,
row.names = FALSE)
}
)
# }
# else{
# downloadHandler(
# filename = function(){paste("plotDownload",'.png',sep='')},
# content = function(file){
# ggsave(file,plot=data1$plot)
# }
# )
# }
}
shinyApp(ui, server)
Try this
library(shiny)
library(ggplot2)
ui <- fluidPage(
radioButtons("view",
label = "View data or plot",
choiceNames = c('Data','Plot'),
choiceValues = c('viewData','viewPlot'),
selected = 'viewData',
inline = TRUE
),
conditionalPanel("input.view == 'viewData'",tableOutput("DF")),
conditionalPanel("input.view == 'viewPlot'",plotOutput("plotDF")),
#downloadButton("download","Download",style = "width:20%;")
uiOutput("plotrtable")
)
server <- function(input, output, session) {
data <- data.frame(Period = c(1,2,3,4,5,6),Value = c(10,20,15,40,35,30))
data1 <- reactiveValues()
inputView <- reactive(input$view) # attempt to make input$view reactive
observeEvent(input$view,{data1$plot <- ggplot(data, aes(Period,Value)) + geom_line()})
output$DF <- renderTable(data)
output$plotDF <- renderPlot(data1$plot)
output$plotrtable <- renderUI({
if(input$view == 'viewData'){downloadButton("download","Download",style = "width:20%;") }
else {downloadButton("downloadp","Download",style = "width:20%;") }
})
output$download <- downloadHandler(
filename = function()
paste("dataDownload","csv",sep="."),
content = function(file){
write.table(
data,
na = "",
file,
sep = ",",
col.names = TRUE,
row.names = FALSE)
}
)
output$downloadp <- downloadHandler(
filename = function(){paste("plotDownload",'.png',sep='')},
content = function(file){
ggsave(file,plot=data1$plot)
}
)
}
shinyApp(ui, server)

Display of only current response in shiny app for storing arbitrary data

I am developing a shiny app which stores the arbitrary data. I have referred This link for the same. I can able to display all the responses including previous responses, but i want to display only the current response, not all response. My code snippet is as given below:
library(shiny)
outputDir <- "C:\\Users/dell/Desktop/"
saveData <- function(data) {
data <- as.data.frame(t(data))
if (exists("responsesiq")) {
responsesiq <<- rbind(responsesiq, data)
} else {
responsesiq <<- data
}
fileName <- "test_irty.csv"
write.csv(
x = responsesiq, sep = ",",
file = file.path(outputDir, fileName),
row.names = FALSE, quote = TRUE
)
}
loadData <- function() {
if (exists("responsesiq")) {
responsesiq
}
}
fields <- c("acc", "staff_name")
shinyApp(
ui = fluidPage(
titlePanel(" System"),
DT::dataTableOutput("responsesiq", width = 300), tags$hr(),
numericInput("acc", "Acc Number", ""),
selectInput("staff_name", "Staff Name",
c("Rajivaksh " = "RT",
"Arvind " = "AKS",
"Ashutosh " = "AS")),
actionButton("submit", "Submit")
),
server = function(input, output, session) {
formData <- reactive({
data <- sapply(fields, function(x) input[[x]])
data
})
observeEvent(input$submit, {
saveData(formData())
})
# Show the previous responses
# (update with current response when Submit is clicked)
output$responsesiq <- DT::renderDataTable({
input$submit
loadData()
})
}
)
Create reactiveValues to hold the current entry, which will trigger when clicking submit
shinyApp(
ui = fluidPage(
titlePanel(" System"),
DT::dataTableOutput("responsesiq", width = 300), tags$hr(),
numericInput("acc", "Acc Number", ""),
selectInput("staff_name", "Staff Name",
c("Rajivaksh " = "RT",
"Arvind " = "AKS",
"Ashutosh " = "AS")),
actionButton("submit", "Submit")
),
server = function(input, output, session) {
formData <- reactive({
data <- sapply(fields, function(x) input[[x]])
data
})
tmp <- reactiveValues(df=NULL)
observeEvent(input$submit, {
saveData(formData())
tmp$df <- t(formData())
})
# Show the previous responses
# (update with current response when Submit is clicked)
output$responsesiq <- DT::renderDataTable({
input$submit
#loadData()
data.frame(tmp$df)
})
}
)

Generating multiple graphs/plots from uploaded files in Shiny

I'm new to Shiny (worked with R for a few months) and i'm trying to figure out what a 'Shiny' way of handling multiple uploaded files is.
My goal is to have an interface where the user can upload multiple files. From these files a report must be generated, in the form of a large number of graphs and plots. From what I learned in the Shiny tutorials, on the server side all objects are isolated from eachother (meaning they cant be read, unless you call another function explicitly).
As you can see in my code below, this means that I have to duplicate the processing code for every single plot. This seems inefficient. What would be the 'Shiny' way of handling this?
Also, i left out a bunch of code that is not absolutely necessary for the example. In essence, I need to do a lot more processing and i dont want to duplicate all that code for every plot.
I am specifically asking about the server-side code. I want to read in multiple files that contain different contents. The operations in the server-side code are just place-holders, I don't actually want to cbind anything but I put that there to keep this code simple. I want to be able to do whatever I want to the imported data frames.
library(shiny)
# Define UI for application
ui <- fluidPage(
# Sidebar with file input
sidebarLayout(
sidebarPanel(
fileInput("people", NULL, multiple = FALSE, accept = ".csv",
buttonLabel = "Browse...", placeholder = "people file"),
fileInput("info", NULL, multiple = FALSE, accept = ".csv",
buttonLabel = "Browse...", placeholder = "info file"),
),
# Show the results of the data processing
mainPanel(
imageOutput("plot"),
tableOutput("base_data")
)
)
)
# Define server logic required to process the data
server <- function(input, output) {
output$base_data <- renderTable({
if(is.null(input$people) | is.null(input$info)) {
} else {
people_file <- input$people
info_file <- input$info
people <- read.csv(people_file$datapath, stringsAsFactors = F, encoding = "UTF-8-BOM")
info <- read.csv(info_file$datapath, stringsAsFactors = F, fileEncoding = "UTF-8-BOM")
rbind(people, info)
}
})
output$plot <- renderImage({
if(is.null(input$people) | is.null(input$info)) {
outfile <- tempfile(fileext='.png')
png(outfile, width = 1200, height = 800, res = 200)
dev.off()
list(src = outfile, width = 1200, height = 800)
} else {
people_file <- input$people
info_file <- input$info
people <- read.csv(people_file$datapath, stringsAsFactors = F, encoding = "UTF-8-BOM")
info <- read.csv(info_file$datapath, stringsAsFactors = F, fileEncoding = "UTF-8-BOM")
outfile <- tempfile(fileext='.png')
png(outfile, width = 1200, height = 800, res = 200)
plot(nrow(people), nrow(info), type="b")
dev.off()
list(src = outfile, width = 1200, height = 800, alt = "questions"))
}
}, deleteFile = TRUE)
}
# Run the application
shinyApp(ui = ui, server = server)
Here's an example of what i want in pseudocode:
[[ui]]
fileInput("people")
fileInput("info")
show(plot)
show(plot2)
show(df)
[[serverside]]
files <- source(input) {
people <- read.csv(input$people, stringsAsFactors = F, encoding = "UTF-8-BOM")
info <- read.csv(input$info, stringsAsFactors = F, encoding = "UTF-8-BOM")
}
contents <- plot(output) {
some_function(files$people, files$info)
plot(contents)
}
contents2 <- plot(output) {
some_other_function(files$people, files$info)
plot2 <- plot(contents2)
}
df <- table(output) {
cbind(files$people, files$info)
}
This is in pseudocode what i have now, which is not efficient.
[[ui]]
fileInput("people")
fileInput("info")
show(plot)
show(plot2)
show(df)
[[serverside]]
contents <- plot(input, output) {
people <- read.csv(input$people, stringsAsFactors = F, encoding = "UTF-8-BOM")
info <- read.csv(input$info, stringsAsFactors = F, encoding = "UTF-8-BOM")
contents <- some_function(people, info)
plot(contents)
}
contents2 <- plot(input, output) {
people <- read.csv(input$people, stringsAsFactors = F, encoding = "UTF-8-BOM")
info <- read.csv(input$info, stringsAsFactors = F, encoding = "UTF-8-BOM")
contents <- some_other_function(people,info)
plot(contents)
}
df <- table(input, output) {
people <- read.csv(input$people, stringsAsFactors = F, encoding = "UTF-8-BOM")
info <- read.csv(input$info, stringsAsFactors = F, encoding = "UTF-8-BOM")
cbind(people, info)
}
I will have to agree with heds1 thats a bit difficult to get your desired result. Since we dont have access to your csvs i created some dummy ones.
Reproducible data / csvs:
write.csv2(x = 1:5, file = "people.csv", row.names = FALSE)
write.csv2(x = 6:10, file = "people2.csv", row.names = FALSE)
If i understand you correctly you would like to avoid repeating the code for every uploaded file.
In order to loop over your files/ datasets we will have to collect them in one data structure.
One way of doing so would be to allow upload multiple files:
fileInput(..., multiple = TRUE)
Ui Side:
The ui side you could create with a loop in renderUI():
output$plots <- renderUI({
lapply(paste("people", 1:length(data)), plotOutput)
})
Server side:
The server side you can create with a loop over:
output[[paste("people", nr)]] <- renderPlot({
plot(plotData)
})
Local assignment
Finally you will have to use local() to avoid that only the data of the last iteration of the loop is taken:
local({
LOCAL_VARIABLE <- data[[nr]]
....
})
Full reproducible example:
library(shiny)
write.csv2(x = 1:5, file = "people.csv", row.names = FALSE)
write.csv2(x = 6:10, file = "people2.csv", row.names = FALSE)
ui <- fluidPage(
fileInput(inputId = "people", label = NULL, accept = ".csv",
buttonLabel = "Browse...", placeholder = "people file", multiple = TRUE),
uiOutput("plots")
)
server <- function(input, output, session) {
observeEvent(input$people, {
data <- lapply(input$people$datapath, read.csv2)
for(nr in 1:length(data)){
local({
plotData <- data[[nr]]
output[[paste("people", nr)]] <- renderPlot({
plot(plotData)
})
})
}
output$plots <- renderUI({
lapply(paste("people", 1:length(data)), plotOutput)
})
})
}
shinyApp(ui, server)
Edit:
Reuse the imported (and transformed) data:
library(shiny)
write.csv2(x = 1:5, file = "people.csv", row.names = FALSE)
ui <- fluidPage(
fileInput(inputId = "people", label = NULL, accept = ".csv",
buttonLabel = "Browse...", placeholder = "people file", multiple = FALSE),
plotOutput("plot"),
tableOutput("table"),
verbatimTextOutput("text")
)
server <- function(input, output, session) {
global <- reactiveValues()
observeEvent(input$people, {
data <- read.csv2(input$people$datapath)
# DO LOTS OF OPERATIONS ON data
global$data <- data
# FROM HERE ON USE: global$data
})
output$plot <- renderPlot({
req(global$data)
plot(global$data)
})
output$table <- renderTable({
global$data
})
output$text <- renderText({
toString(global$data)
})
}
shinyApp(ui, server)

How to loop through multiple upload widegets in shiny?

I want to create multiple fileInput function to allow users to upload files. The main reason I am creating multiple upload widgets is because I want to allow users to upload through different path. What I am trying to accomplish here is to loop through all the fileInputs and save all the files into one dataframe but not able to do it in example of my code.
library(shiny)
library(data.table)
library(DT)
n_attachments <- sprintf("file%s",seq(1:2))
ui <- fluidPage(
titlePanel('File download'),
sidebarLayout(
sidebarPanel(
textInput("LOAN_NUMBER", label = "Fannie Mae Loan Number", placeholder = "Please enter loan #")
, textInput("REO_ID", label = "REO Number", placeholder = "Please enter REO #")
, fileInput("file1", "Attachments1", accept = c("text/csv", "text/comma-separated-values,text/plain",".csv", ".pdf", ".doc", ".xlsx"), multiple = TRUE)
, fileInput("file2", "Attachments2", accept = c("text/csv", "text/comma-separated-values,text/plain",".csv", ".pdf", ".doc", ".xlsx"), multiple = TRUE)
, textOutput('text')
),
mainPanel(
DT::dataTableOutput("table"), tags$hr()
)
)
)
server <- function(input, output) {
bin_data <- reactive({
attachement_data <- data.frame(ATTACHMENT = character(), FILENAME = character(), LOAN_NUMBER = character(), REO_ID = character())
for(x in n_attachments)
{
output$text <- renderText({ input$x })
req(input$x)
# binary_data <- paste(readBin(input$file1$datapath, what="raw", n=1e6), collapse="-")
# attachment_info <- data.frame(ATTACHMENT = binary_data, FILENAME = paste0(input$file1$name))
# attachment_info
binary_data=list()
filenames=list()
for(i in 1:length(input$x[,1])){
binary_data[[i]] <- paste(readBin(input$x[[i, 'datapath']], what = "raw", n=1e6), collapse = "-")
filenames[[i]] <- input$x[[i, 'name']]
}
bin_data_frame <- data.frame(ATTACHMENT = as.character(unlist(binary_data)), FILENAME = as.character(unlist(filenames)))
bin_data_frame$LOAN_NUMBER <- input$LOAN_NUMBER
bin_data_frame$REO_ID <- input$REO_ID
attachement_data <- rbind(attachement_data, bin_data_frame)
}
save(attachement_data, file="attachement_data.RData")
attachement_data
})
output$table <- DT::renderDataTable({
bin_data()
})
}
shinyApp(ui = ui, server = server)
ok I think I figured it out, I have to use input[[x]] instead of input$x, and I added couple lines to check how many fileinputs are uploaded.
server <- function(input, output) {
bin_data <- reactive({
attachement_data <- data.frame(ATTACHMENT = character(), FILENAME = character(), LOAN_NUMBER = character(), REO_ID = character())
k <- 0
for(x in n_attachments)
{
if(!is.null(input[[x]]))
{
k = k + 1
}
}
for(x in n_attachments[0:k])
{
if(!is.null(input[[x]]))
{
output$text <- renderText({ input[[x]] })
req(input[[x]])
# binary_data <- paste(readBin(input$file1$datapath, what="raw", n=1e6), collapse="-")
# attachment_info <- data.frame(ATTACHMENT = binary_data, FILENAME = paste0(input$file1$name))
# attachment_info
binary_data=list()
filenames=list()
for(i in 1:length(input[[x]][,1])){
binary_data[[i]] <- paste(readBin(input[[x]][[i, 'datapath']], what = "raw", n=1e6), collapse = "-")
filenames[[i]] <- input[[x]][[i, 'name']]
}
bin_data_frame <- data.frame(ATTACHMENT = as.character(unlist(binary_data)), FILENAME = as.character(unlist(filenames)))
bin_data_frame$LOAN_NUMBER <- input$LOAN_NUMBER
bin_data_frame$REO_ID <- input$REO_ID
attachement_data <- rbind(attachement_data, bin_data_frame)
}
}
save(attachement_data, file="attachement_data.RData")
attachement_data
})
output$table <- DT::renderDataTable({
bin_data()
})
}
shinyApp(ui = ui, server = server)

Resources