Download csv in shiny using inputs as data frame name parts - r

There are existing data.frames that are called: active_acc_newacc, active_acc_oldacc, inactive_acc_newacc, inactive_acc_oldacc. They all contain a lot of rows and columns. I created a code with Shiny, where the user chooses with a radio button if they want to download active or inactive and old, or new. When they click on Download, a csv file needs to be downloaded. The csv should be the content of the dataframe, for example the user chooses Active and new, then the dataframe called active_acc_newacc should be downloaded as a csv, another example is if they chose Inactive and old, then the dataframe called inactive_acc_oldaccdataframe should be downloaded as csv.
In this example, active_acc_newacc contains this sample data:
structure(list(Alpha = c(0.192491906485068, -1.44670180633351,
-0.323180534047634, 1.62229611652493, -0.689024123596357, 2.04212222261495,
0.94377911190294, 2.0819268787991, 1.91711727878331, -0.414812239592928
), Beta = c(1.03285349943413, -1.67856959219527, 0.157549690345431,
1.48913611644558, -0.0757895625491196, 1.27178094415894, 0.641673407672177,
0.800761254937157, 1.86265922566283, -0.545356026768875), Gamma = c(1.52068837343838,
-3.61004798325456, -1.35857038834863, 3.48938862108709, -3.05109504225968,
6.5047022366346, 2.50727319977214, 5.31673927920108, 3.69096202696173,
-1.03802874828505)), row.names = c(NA, -10L), class = "data.frame")
I have the following in the ui.R, requesting input:
ui = fluidPage(
titlePanel("Account classification"),
sidebarLayout(
sidebarPanel(
radioButtons("account_status","Select account status", choices=c("Active","Inactive","Include both"),selected = "Active"),
br(),
radioButtons("account_age","Select account creation time", choices=c("old","new","Created any time"),selected = "new")
),
mainPanel(
downloadButton('downloadData', 'Download')
)
)
)
I created the following server.R
server = function(input, output) {
datasetInput <- reactive({
switch(input$account_status,
"Active" = active_acc,
"Inactive" = inactive_acc,
"Include both" = NULL)
switch(input$account_age,
"old" = oldacc,
"new" = newacc,
"Created any time" = all)
})
dfname <- reactive({
paste(input$account_status,input$account_age, sep='_')
})
output$downloadData <- downloadHandler(
filename = function() { paste(input$dataset, '.csv', sep='') },
content = function(file) {
write.csv(dfname(), file)
}
)
}
shinyApp(ui, server)
Unfortunately, it does not work, when I hit the "download" button, it downloads a csv that contains this (in case of inactive and old are selected) :
"","x"
"1","Inactive_old”
So only the text is written out.
I modified the code and sometimes what Download does is downloading the ui.R radio button page as an html.
How shall I write the codes in order to get a successful csv download?

How about this:
Activeacc_newacc <- structure(list(Alpha = c(0.192491906485068, -1.44670180633351,
-0.323180534047634, 1.62229611652493, -0.689024123596357, 2.04212222261495,
0.94377911190294, 2.0819268787991, 1.91711727878331, -0.414812239592928
), Beta = c(1.03285349943413, -1.67856959219527, 0.157549690345431,
1.48913611644558, -0.0757895625491196, 1.27178094415894, 0.641673407672177,
0.800761254937157, 1.86265922566283, -0.545356026768875), Gamma = c(1.52068837343838,
-3.61004798325456, -1.35857038834863, 3.48938862108709, -3.05109504225968,
6.5047022366346, 2.50727319977214, 5.31673927920108, 3.69096202696173,
-1.03802874828505)), row.names = c(NA, -10L), class = "data.frame")
ui = fluidPage(
titlePanel("Account classification"),
sidebarLayout(
sidebarPanel(
radioButtons("account_status","Select account status", choices=c("Active","Inactive","Include both"),selected = "Active"),
br(),
radioButtons("account_age","Select account creation time", choices=c("old","new","Created any time"),selected = "new")
),
mainPanel(
downloadButton('downloadData', 'Download')
)
)
)
server = function(input, output) {
datasetInput <- reactive({
switch(input$account_status,
"Active" = active_acc,
"Inactive" = inactive_acc,
"Include both" = NULL)
switch(input$account_age,
"old" = oldacc,
"new" = newacc,
"Created any time" = all)
})
dfname <- reactive({
test <- paste0(input$account_status, "acc_", input$account_age, "acc", sep='')
get(test)
})
output$downloadData <- downloadHandler(
filename = function() { paste(input$dataset, '.csv', sep='') },
content = function(file) {
write.csv(dfname(), file)
}
)
}
shinyApp(ui, server)

Related

In RShiny using renderPrint/renderText to show error when expected files are missing in downloadHandler

I am using a downloadHandler in a shiny app which let's me download 4 reactive dataframes that has been created. I want to include a functionality if either one of the dataframes has not been created then that will be printed in renderPrint/renderText saying one of the dataframes is missing. A minimal example from the server code is given below but it does not work the way I want it to.
output$dl <-
if((df1()!= '') || (df2()!='') || (df3()!= '') || (df4()!= '') ){
downloadHandler(
filename = "New_Data.xlsx",
content = function(file){
write_xlsx(list("S1" = df1(), "S2" = df2(), "S3" = df3(), "S4" = df4()), path = file)
}else{
output$dl_error <- renderPrint({'One of the dataframes is missing'})
}
)
or something like this
data_list <- reactive({
list("S1" = df1(),
"S2" = df2(),
"S3" = df3(),
"S4" = df4())
})
output$dl <- reactive({
if(length(data_list()==4)){
downloadHandler(
filename = "New_Data.xlsx",
content = function(file){
write_xlsx(data_list(), path = file)
}
)
}else{
output$dl_error <- renderPrint({'One of the dataframes is missing'})
}
})
It will be great if someone can help me out with this. Also, it would be better if the error message in the renderPrint/renderText shows which dataframe is missing.
As said in my comment, you could disable the button. You can use the shinyjs package to do that easily. Another option is to hide the download button with a conditionalPanel. Here is a third option. I hide the download button with a conditionalPanel and instead I display a "fake" download button. When the dataframe is available, the fake data button is hidden and the true download button becomes visible. If the user clicks on the fake download button, he is told that the dataframe is not available with an alert.
library(shiny)
library(shinyalert)
library(writexl)
ui <- fluidPage(
useShinyalert(),
br(),
conditionalPanel(
condition = "!output.ok",
actionButton("fake", "Download", icon = icon("save"))
),
conditionalPanel(
condition = "output.ok",
style = "display: none;",
downloadButton("dwnld", "Download", icon = icon("save"))
),
br(),
actionButton("databtn", "Generate dataframe")
)
server <- function(input, output, session){
observeEvent(input[["fake"]], {
shinyalert(
title = "Error!",
text = "The dataframe is not ready yet",
type = "error"
)
})
df <- reactiveVal(NULL)
observeEvent(input[["databtn"]], {
df(iris)
})
output[["ok"]] <- reactive({
!is.null(df())
})
outputOptions(output, "ok", suspendWhenHidden = FALSE)
output[["dwnld"]] <- downloadHandler(
filename = "iris.xlsx",
content = function(file){
write_xlsx(list(iris = df()), path = file)
}
)
}
shinyApp(ui, server)

downloadHandler in shiny in a loop

I am trying to build a shiny app to retrieve data from an Oracle table based on user specified ID. I want to create one file with data for each ID and download it to the default downloads folder. I would also like to zip the files and provide the user with that one file. Also, the app is just to download the data and I really don't want a main Panel hence the width of the mainPanel is zero. If that's not the way to do it, please let me know.The app is going to reside on a server and hence the need for a download Handler. Below is my code. Any help is greatly appreciated.
library(shiny)
library(ROracle)
library(shinyjs)
library(shinyalert)
library(shinyWidgets)
ui <- fluidPage(
useShinyjs(),
useShinyalert(),
# Application title
titlePanel(fluidRow(
column(10, "RAINFALL AND ET DATA RETRIEVAL",align="center"),
column(2, offset = 0,img(height =90,width=250,src="logo.png",align="left"))
)),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(width = 12,
helpText("Please enter IDs separated by commas. You can enter up to 25 IDs."),
textInput("PixelID", "Pixel ID(s)", value = ""),
helpText("OR"),
fileInput('datafile','Choose csv file to upload Pixel IDs.The csv file should have Pixel IDs in the first column WITHOUT ANY HEADER.',accept = c('csv','comma-separated-values','.csv')),
helpText("Please select a parameter you would like to retrieve."),
radioButtons("ParameterType", "Parameters",
choices = c("Rainfall Estimates","Evapotranspiration Estimates"),
selected = "None"),
dateInput("startdate","Data From", format = "yyyy-mm-dd",max = Sys.Date()),
dateInput("enddate","Data To", format = "yyyy-mm-dd",max = Sys.Date()),
br(), br(),
actionBttn("goButton","Go!",color = "default",style = "fill",size = "lg"),
br(),br(),
uiOutput("download"),
mainPanel(width=0)
)
)
server <- function(input, output, session) {
data<-eventReactive(input$goButton,{
if(is.null(input$PixelID) || input$PixelID == ""){
req(input$datafile)
infile<-input$datafile
PixelList<-read.table(infile$datapath, header = FALSE, sep = ",", stringsAsFactors = FALSE)
colnames(PixelList)<-"PixelNum"
PixelList_comma<-paste(PixelList$PixelNum, collapse = ",")} else{
if(input$PixelID != ""){
PixelList<-data.frame(strsplit(input$PixelID,","))
colnames(PixelList)<-"PixelNum"
PixelList_comma<-input$PixelID
}}
drv <- dbDriver("Oracle")
connection <- dbConnect(drv, username = "xxxx", password = "xxxxx", dbname = "xxxx")
if(input$ParameterType=="Rainfall Estimates"){
for(i in 1:nrow(PixelList)){
raindata<-dbGetQuery(connection, paste("select PIXEL, TO_CHAR(tsdatetime_dt, 'MM/DD/YYYY HH24:MI') as DATE_TIME, tsvalue_ms as RAINFALL from xxxx
where feature_id =",PixelList[i,1]," order by tsdatetime_dt", sep=""))
}
dbDisconnect(conn = connection)
}
return(raindata)
})
output$download <- renderUI({
downloadButton("downloadData", "Download")
})
output$downloadData <- downloadHandler(
filename = function() {
paste("testnexrad",".zip",sep = "")
},
content = function(file) {
for(i in 1:nrow(PixelList)){
#No idea what to do here
}
}
)
}
# Run the application
shinyApp(ui = ui, server = server)

Add Delete and Edit Buttons to a form that creates a DT:dataTable in Shiny

I created a form in shiny using different inputs in the server part of the app. I am now trying to add two buttons to the form but haven't found the right way to do it. I need one button that allows the user to edit a selected entry on the table, and another button that allows the user to remove the selected entry from the table, and of course once this is done the datatable needs to be updated.
Here is a reproducible example. I am going of this example mostly with a few modifications https://deanattali.com/2015/06/14/mimicking-google-form-shiny/
My app code:
library(shiny)
library(tidyverse)
library(shinyWidgets)
# Define the fields we want to save from the form
fields <- c("q1", "q2", "q3", "q4", "q5", "q6")
# Save a response
# This is one of the two functions we will change for every storage type
saveData <- function(data) {
data <- as.data.frame(t(data))
if (exists("responses")) {
responses <<- rbind(responses, data)
} else {
responses <<- data
}
}
# Load all previous responses
# This is one of the two functions we will change for every storage type
loadData <- function() {
if (exists("responses")) {
responses
}
}
# Shiny app with 3 fields that the user can submit data for
shinyApp(
ui = fluidPage(
tags$br(),
dropdown(
htmlOutput("q1"),
htmlOutput("q2"),
htmlOutput("q3"),
htmlOutput("q4"),
htmlOutput("q5"),
htmlOutput("q6"),
actionButton("submit", "Submit"),
actionButton("edit", "Edit"),
style = "unite",
icon = icon("plus"),
status = "danger",
#width = "300px",
size = "m",
label = "Add new Record",
tooltip = TRUE,
animate = animateOptions(
enter = animations$fading_entrances$fadeInLeftBig,
exit = animations$fading_exits$fadeOutRightBig
)
),
tags$hr(),
downloadButton("downloadData", "Download"),
actionButton("deleteRow", "Delete Row"),
tags$hr(),
column(width = 12, DT::dataTableOutput("responses", width = '100%'))
),
server = function(input, output, session) {
output$q1 <- renderUI({
textInput("Q1", "...", "")
})
output$q2 <- renderUI({
textInput("Q2", "...", "")
})
output$q3 <- renderUI({
dateInput("Q3", "...")
})
output$q4 <- renderUI({
textAreaInput("Q4", "...")
})
output$q5 <- renderUI({
textAreaInput("Q5", "...")
})
output$q6 <- renderUI({
dateInput("Q6", "...")
})
# 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())
})
# Show the previous responses
# (update with current response when Submit is clicked)
output$responses <- DT::renderDataTable({
input$submit
loadData()
})
# Downloadable csv of selected dataset ----
output$downloadData <- downloadHandler(
filename = function() {
paste("questionnaire", ".csv", sep = "")
},
content = function(file) {
write.csv(loadData(), file, row.names = FALSE)
}
)
}
)
I added the actionlink buttons for Edit and Delete but need some help with programmatically side of things in the server.
Thank you,
Welcome to stack overflow. It would be helpful to go over some reactive programming. Here a global df is defined to hold the original dataframe.
This dataframe is modified when submit or delete are pressed.
Similarly the download handler is updated when the buttons are pressed.
library(shiny)
library(tidyverse)
library(shinyWidgets)
# Define the fields we want to save from the form
fields <- c("q1", "q2", "q3", "q4", "q5", "q6")
# Shiny app with 3 fields that the user can submit data for
shinyApp(
ui = fluidPage(
tags$br(),
dropdown(
textInput("Q1", "...", ""),
textInput("Q2", "...", ""),
textInput("Q3", "...", ""),
textInput("Q4", "...", ""),
textInput("Q5", "...", ""),
textInput("Q6", "...", ""),
actionButton("submit", "Submit"),
actionButton("edit", "Edit"),
style = "unite",
icon = icon("plus"),
status = "danger",
#width = "300px",
size = "m",
label = "Add new Record",
tooltip = TRUE,
animate = animateOptions(
enter = animations$fading_entrances$fadeInLeftBig,
exit = animations$fading_exits$fadeOutRightBig
)
),
tags$hr(),
downloadButton("downloadData", "Download"),
actionButton("deleteRow", "Delete Row"),
tags$hr(),
column(width = 12, DT::dataTableOutput("responses", width = '100%'))
),
server = function(input, output, session) {
#initialiez a dataframe
df = data.frame(Q1 = character(0),
Q2 = character(0),
Q3 = character(0),
Q4 = character(0),
Q5 = character(0),
Q6 = character(0))
#Modify the dataframe when submit is clicked
observeEvent(input$submit,{
data = data.frame(Q1 = input$Q1,
Q2 = input$Q2,
Q3 = input$Q3,
Q4 = input$Q4,
Q5 = input$Q5,
Q6 = input$Q6)
df <<- rbind(df,data)
})
#Delete a row when clicked
observeEvent(input$deleteRow,{
df <<- df%>%
filter(row_number() < nrow(.))
})
# Show the previous responses
# (update with current response when Submit is clicked)
output$responses <- DT::renderDataTable({
#simply to induce reactivity
input$submit
input$deleteRow
return(df)
})
#Update the download handler then submit is clicked
observe({
input$submit
input$deleteRow
# Downloadable csv of selected dataset ----
output$downloadData <- downloadHandler(
filename = function() {
paste("questionnaire", ".csv", sep = "")
},
content = function(file) {
write.csv(df, file, row.names = FALSE)
}
)
})
}
)

Shiny DownloadHandler unable to save .csv file

I'm trying to download my datatable into, a csv. file. Unfortuantely, even though the download starts, it's stuck with calculating and doesn't save the data. The file size is 8mb large and I only could workaround this issue with downloading only the filtered dataset. I also tried setting the donload size to 10 mb with shiny.maxRequestSize=30*1024^2
I really need the option to save the whole dataset. If anyone could provide some insights I would much appreciate it (And yes, I run the App in the Browser)
my ui function looks like this:
tbl <- read.csv(file.choose(new = FALSE), header = TRUE, sep = ",", row.names=1)
ui <- navbarPage(
title = "Data Table Options",
#Tab with the dataset table
tabPanel("Lot Dataset",
div(h3("Download"), style = "color:blue"),
helpText(" Select the download format"),
radioButtons("type", "Format type:",
choices = c("Excel (CSV)", "Text (Space Separated)", "Doc")),
helpText(" Click on the download button to download the Lot Dataset"),
downloadButton("download_filtered", "Download Filtered Data"),
br(),
br(),
br(),
DT::dataTableOutput("dt"), #datatable
),
)
my server function like this:
server <- function(session, input, output) {
#Increasing Downloadsize to 10MB
options(shiny.maxRequestSize=10*1024^2)
#render the datatable
output$dt <- DT::renderDataTable({
datatable(tbl, filter = "top", options = list(
lengthMenu = list(c(25, 50, 100, -1), c("25", "50", "100", "All")),
pageLength = 25))
})
#bottom panel with row indices
output$filtered_row <-
renderPrint({
input[["dt_rows_all"]]
})
#file extension for download
fileext <- reactive({
switch(input$type,
"Excel (CSV)" = "csv", "Text" = "txt", "Doc" = "doc")
})
#downloadHandler() for file download of Lot Dataset
output$download_filtered <- downloadHandler(
filename = function() {
paste("MLdataset_test", fileext(), sep=".") #filename
},
content = function(file) {
#write tbl with filter
write.csv(tbl[input[["dt_rows_all"]], ],
file = file, row.names = F)
}
)
}
Any help appreciated!!!

Error-free download of reactive objects in Shiny

Shiny experts!
In our application we have download button for plot downloading. The button is working only when some data are loaded and processed. When you push the button before, there is an error message from plotting function, because it has no data.
content = function(file) {
r <- rChart_line_plot(follow_view_func(),log_scale = input$checkbox_log_scale_plot,isRel = input$checkboxRelativeTab2)
r$save(file, standalone = TRUE)
}
We want to make our app foolproof and error-free. Is there any possible way to send to downloadHandler's content "NULL"? This don't work.
content = function(file) {
if ( "our data are ready for printing" ) {
r <- rChart_line_plot(follow_view_func(),log_scale = input$checkbox_log_scale_plot,isRel = input$checkboxRelativeTab2)
r$save(file, standalone = TRUE)
} else {
NULL
}
}
And we're getting:
Error opening file: 2
Error reading: 9
Is there something like validate() function with even information for user "Please load file first"
Thank You a lot.
You are correct that you want a validate statement. Here is a link with descriptions from the RStudio team. This will allow you to have a more informative error message. Your complete downloadHandler function would look something like the following. Note that this assumes your dataset could be null.
output$Download <- downloadHandler(
filename = function() {
paste("test.png",sep="")
},
content = function(file) {
myData <- follow_view_func()
validate(
need(!is.null(myData), "Please select valid dataset")
)
r <- rChart_line_plot(myData,log_scale = input$checkbox_log_scale_plot,isRel = input$checkboxRelativeTab2)
r$save(file, standalone = TRUE)
}
)
Here is a complete reproducible example with the iris dataset.
library(shiny)
library(rCharts)
runApp(
list(
ui = pageWithSidebar(
headerPanel("Using 'validate' for useful error messages"),
sidebarPanel(
selectInput("dataset", "Choose a dataset:",
choices = c("null", "iris")),
selectInput(inputId = "x",
label = "Choose X",
choices = c('SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth'),
selected = "SepalLength"),
selectInput(inputId = "y",
label = "Choose Y",
choices = c('SepalLength', 'SepalWidth', 'PetalLength', 'PetalWidth'),
selected = "SepalWidth"),
downloadButton("Download")
),
mainPanel(
showOutput("myChart", "polycharts")
)
),
server = function(input, output) {
datasetInput <- reactive({
switch(input$dataset,
"iris" = iris,
"null" = NULL)
})
myChart <- reactive({
myData <- datasetInput()
validate(
need(!is.null(myData), "Please select valid dataset")
)
names(myData) = gsub("\\.", "", names(myData))
p1 <- rPlot(input$x, input$y, data = myData, color = "Species",
facet = "Species", type = 'point')
p1$addParams(dom = 'myChart')
return(p1)
})
output$myChart <- renderChart({myChart()})
output$Download <- downloadHandler(
filename = function() {
paste("test.png",sep="")
},
content = function(file) {
p1 <- myChart()
p1$save(file, standalone = TRUE)
}
)
}
)
)
UPDATE
As per the OP request, it may be ideal to have no error whatsoever with the download button. The only solution I could come up with is to make the button a conditionalPanel. This intuitively makes sense to me because why would you download if there is nothing on the screen? The only change in the code above needed for this is to change:
downloadButton("Download")
to
conditionalPanel("output.myChart", downloadButton("Download"))
Now the download button will only be present when a valid chart is created.

Resources