RShiny: How to pass variable from Server.R to an RMD script - r

I am playing around with RShiny recently, and I've built a working web interface that takes two parameters "date" and "location" and gives me back a series of graphs and tables from our database that fit the criteria.
What I would like to do with that, is to have users being able to download all the data and graphs in the form of a RMD report in HTML format.
so I have
1. UI.R with a download button
2. Server.R's downloadHandler starts my RMD script
3. ????
UI.R
downloadButton('downloadData','Download')
Server.R
output$downloadData<- downloadHandler(filename ="myData.html",
content= function(file= NULL){
knit(thread.RMD)
}

Here is the answer I got from the Shiny Google Group : https://groups.google.com/forum/?fromgroups=#!topic/shiny-discuss/XmW9V014EtI
The function that's given as the 'content' argument to downloadHandler takes one option, 'file'. When the download button is clicked, the download handler calls that function, and it uses the file argument to tell it where is should save the output file.
I don't see a way to set the output file name from knit2html(), but you can just rename it after it's created:
output$downloadData <- downloadHandler(
filename ="ShinyData.html",
content = function(file) {
knit2html("myreport.rmd")
file.rename("myreport.html", file)
}
)
(Also, you're missing a closing parenthesis in ui.r.)
-Winston

Related

Generate and render rmd from within shinyapp

I wrote a shiny app that includes generating an rmd file and then rendering it into html report.
As shown in the simple example below, there is a variable created inside the server function of shinyapp, then the created rmd file should have access to this variable while rendering into html.
according to other posts and articles, it seems that we have to copy the rmd file into a temporary folder to work and that's what I tried to do below.
the app is not working.
when I try locally, I get this error:
Quitting from lines 8-9 (x3.Rmd)
Warning: Error in print: object 'xz' not found
so it seems that shiny was able to find the generated rmd, started rendering it but did not have access to the xz variable generated in shiny. I did some reading, and it seems to be related to the render environment (ie it renders in a new session) but I do not know how to fix that. (in the real app I am working on, the render process should have access to a dataframe not just a string variable, but I believe the concept is the same for illustration purpose).
when I tested on shinyapps.io, it says Failed-server problem when I click on the download button. Surprisingly, there is nothing in the application log, it says currently no logs.
when I test the original app I am writing (not this simple example), I get this error in shinyapp.io logs:
2022-04-10T18:01:45.357461+00:00 shinyapps[6055802]: Warning in normalizePath(path, winslash = winslash, mustWork = mustWork) :
2022-04-10T18:01:45.357710+00:00 shinyapps[6055802]: [No stack trace available]
2022-04-10T18:01:45.357627+00:00 shinyapps[6055802]: Warning: Error in abs_path: The file '/tmp/Rtmp27RVU8/x3.Rmd' does not exist.
2022-04-10T18:01:45.357543+00:00 shinyapps[6055802]: path[1]="/tmp/Rtmp27RVU8/x3.Rmd": No such file or directory
My goal is to make this work from shinyapp.io. Any suggestions on where to go from there, I believe there are two issues:
1- make sure the rmd render will have access to the variables generated in shiny
2- save the rmd file in a place that is "convenient" for shiny to find and render.
library(shiny)
ui <- fluidPage(
downloadButton("report", "Download sample report.")
)
server <- function(input, output, session) {
#create content and export it into rmd
observe({
xz= "hello there"
x3 = '---
title: sample
output: html_document
---
``` {r}
print(xz)
```
';write(x3, file="x3.rmd", append = FALSE)
})
#Render the report and pass it to download handler
output$report <- downloadHandler(
filename = "sample.html",
content = function(file) {
tempReport <- file.path(tempdir(), "x3.Rmd")
file.copy("x3.Rmd", tempReport, overwrite = TRUE)
output <- rmarkdown::render(
input = tempReport
)
file.copy(output, file)
})
}
shinyApp(ui, server)
These are the resources I used (but still unable to make it work). All deal with parameterized reports of a user uploaded .rmd, which won't work in my case:
https://shiny.rstudio.com/articles/generating-reports.html
https://community.rstudio.com/t/where-do-i-save-the-rmd-file-i-am-generating-with-a-shiny-r-app/65987/3
https://community.rstudio.com/t/generating-downloadable-reports/39701
https://mastering-shiny.org/action-transfer.html#downloading-reports
I have done this with Shiny App but on a shiny server (in my work place) and can share only the approach here. Currently the access to exact code will take time but this shall give you idea. No idea how it works with shinyapps.io but will try that sometime.
Within UI of shiny provide for a "Generate Report" Button.
In server, with
observeEvent(input$btn_Id,{
#Code to render the html in rmarkdown and then also `downloadHandler()`
})
Please check this also :
Use the downloandHanlder() referring this documentation.
This one gives idea to download data
Solution to passing variables is not special. Just ensure data is already present when you are calling render()
like this:
rmarkdown::render(input = "D:/YourWorkingDirectly/Letters.rmd")
If this doesn't help you , please let me know and will delete the answer.

Using Shiny fileInput to get path only

I have a very large fixed width file I need to read in using my Shiny application. The way my program is currently structured is the ui.R contains a fileInput allowing the user to locate the file using the browser.
On the server side, I only capture the path to the file such as the following:
path2file <- reactive({
infile <- input$path2file
if (is.null(infile)) return(NULL)
infile$datapath
})
A subsequent function takes that path as input and the proceeds to read in the file according to its layout specifications. This all works just fine; however when dealing with extremely large fwf files my program slows down tremendously and takes hours to get the path name of the file read in using fileInput
My suspicion is that fileInput is actually reading in the entire file and then my function only returns the datapath even though I am not explicitly reading in any file format type within the function.
My aim is to continue using the program as I have it structured now and obtain only the path to this file using my fileInput. I have found this topic on SO, and see it is a possible option.
Getting file path from Shiny UI (Not just directory) using browse button without uploading the file
However, I also aim to minimize the number of package dependencies I have; this has become a big issue and so if I MUST use an additional package I will, but I'd like to avoid that at all costs.
I experimented with this cheap trick:
path2file <- reactive({
infile <- input$path2file
if (is.null(infile)) return(NULL)
scan(infile$datapath, n = 1)
infile$datapath
})
Thinking that it would be a fast workaround, but it too is extremely slow so I suspect it too is not reading in only n = 1. So, my question is can anyone identify a way to use fileInput to allow a user to locate a file and have the server side function capture only the path and NOT read in the file or try and parse it in any way? More importantly, can this be done using functions in base R and Shiny alone without having to grab functions from other extended packages?
The above is the relevant portion of code in the server.R file and the relevant portion of code in the ui.R file is
fileInput('path2dor', 'Choose the DOR .txt file to format',
accept=c('text/csv',
'text/comma-separated-values,text/plain', '.csv')),
Thank you for you advice.
This functionality is not possible with fileInput. The reason is because 'fileInput' do not provide local path information to the server for security reasons.
With fileInput the user navigates through the browser on his local machine, the resulting file on the server side is the uploaded copy of the selected local once.
As an alternative you can use the shinyFiles package, which do navigate through the server side. This means, that you get all the paths on your local machine.
A second alternative could be a simple text input, which lets the user add a path by hand (make sure to check the path on the server side to not run into any troubles).
As pointed by others, due to security concerns shiny creates a tmp folder with all files loaded called with fileinput
Accordingly, you need to select all the files of interest in your folder and then call this tmp file with ...$datapath
Note however that each element of datapath will include both the directory information and the corresponding file name. Accordingly, you need to trim those paths to only account for the tmp directory. This can be achieved as follows...
Assume you will create an object called phu which will only contain the first file in the folder (using input$upload$datapath[1]) you called with fileInput("upload", NULL, buttonLabel = "Upload...", multiple = TRUE)
phu<-as.character(input$upload$datapath[1])
phu<-substr(phu,1,nchar(phu)-5)
The second line removes the last five characters in the string. These characters are 0.txt or whatever other extension you called in your input. The code provided only works with .txt files and requires the tm package. You can now use the object phu as the input directory of interest.
Finally, you need to call this output with an output object and print it in your ui this is shown with textOutput("Pdiretory") below.
The following example shows the entire process. Note that there are no security concerns because this temporary file and its content will be deleted at closing. Once more, the input files are .txt files.
library(shiny)
library(tm)
ui <- fluidPage(
fileInput("upload", NULL, buttonLabel = "Upload...", multiple = TRUE),
textOutput("Pdiretory")
)
server <- function(input, output, session) {
listdir <- eventReactive(input$upload, {
phu<-as.character(input$upload$datapath[1])
phu<-substr(phu,1,nchar(phu)-5)
txt<-Corpus(DirSource(phu),readerControl = list(language = "en"))
print(txt)
})
output$Pdiretory <- renderPrint ({
listdir()
})
}
shinyApp(ui = ui, server = server)

Reading an RData file into Shiny Application

I am working on a shiny app that will read a few RData files in and show tables with the contents. These files are generated by scripts that eventually turns the data into a data frame. They are then saved using the save() function.
Within the shiny application I have three files:
ui.R, server.R, and global.R
I want the files to be read on an interval so they are updated when the files are updated, thus I am using:
reactiveFileReader()
I have followed a few of the instructions I have found online, but I keep getting an error "Error: missing value where TRUE/FALSE is needed". I have tried to simplify this so I am not using:
reactiveFileReader()
functionality and simply loading the file in the server.R (also tried in the global.R file). Again, the
load()
statement is reading in a data frame. I had this working at one point by loading in the file, then assigning the file to a variable and doing an "as.data.table", but that shouldn't matter, this should read in a data frame format just fine. I think this is a scoping issue, but I am not sure. Any help? My code is at:
http://pastebin.com/V01Uw0se
Thanks so much!
Here is a possible solution inspired by this post http://www.r-bloggers.com/safe-loading-of-rdata-files/. The Rdata file is loaded into a new environment which ensures that it will not have unexpected side effect (overwriting existing variables etc). When you click the button, a new random data frame will be generated and then saved to a file. The reactiveFileReader then read the file into a new environment. Lastly we access the first item in the new environment (assuming that the Rdata file contains only one variable which is a data frame) and print it to a table.
library(shiny)
# This function, borrowed from http://www.r-bloggers.com/safe-loading-of-rdata-files/, load the Rdata into a new environment to avoid side effects
LoadToEnvironment <- function(RData, env=new.env()) {
load(RData, env)
return(env)
}
ui <- shinyUI(fluidPage(
titlePanel("Example"),
sidebarLayout(
sidebarPanel(
actionButton("generate", "Click to generate an Rdata file")
),
mainPanel(
tableOutput("table")
)
)
))
server <- shinyServer(function(input, output, session) {
# Click the button to generate a new random data frame and write to file
observeEvent(input$generate, {
sample_dataframe <- data.frame(a=runif(10), b=rnorm(10))
save(sample_dataframe, file="test.Rdata")
rm(sample_dataframe)
})
output$table <- renderTable({
# Use a reactiveFileReader to read the file on change, and load the content into a new environment
env <- reactiveFileReader(1000, session, "test.Rdata", LoadToEnvironment)
# Access the first item in the new environment, assuming that the Rdata contains only 1 item which is a data frame
env()[[names(env())[1]]]
})
})
shinyApp(ui = ui, server = server)
Ok - I figured out how to do what I need to. For my first issue, I wanted the look and feel of 'renderDataTable', but I wanted to pull in a data frame (renderDataTable / dataTableOutput does not allow this, it must be in a table format). In order to do this, I found a handy usage of ReportingTools (from Bioconductor) and how they do it. This allows you to use a data frame directly and still have the HTML table with the sorts, search, pagination, etc.. The info can be found here:
https://bioconductor.org/packages/release/bioc/html/ReportingTools.html
Now, for my second issue - updating the data and table regularly without restarting the app. This turned out to be simple, it just took me some time to figure it out, being new to Shiny. One thing to point out, to keep this example simple, I used renderTable rather than the solution above with the ReportingTools package. I just wanted to keep this example simple. The first thing I did was wrap all of my server.R code (within the shinyServer() function) in an observe({}). Then I used invalidateLater() to tell it to refresh every 5 seconds. Here is the code:
## server.R ##
library(shiny)
library(shinydashboard)
library(DT)
shinyServer(function(input, output, session) {
observe({
invalidateLater(5000,session)
output$PRI1LastPeriodTable <- renderTable({
prioirtyOneIncidentsLastPeriod <- updateILP()
})
})
})
Now, original for the renderTable() portion, I was just calling the object name of the loaded .Rdata file, but I wanted it to be read each time, so I created a function in my global.R file (this could have been in server.R) to load the file. That code is here:
updateILP <- function() {
load(file = "W:/Projects/R/Scripts/ITPOD/itpod/data/prioirtyOneIncidentsLastPeriod.RData", envir = .GlobalEnv)
return(prioirtyOneIncidentsLastPeriod)
}
That's it, nothing else goes in the global.R file. Your ui.R would be however you have it setup, call tableOutout, dataTableOutput, or whatever your rendering method is in the UI. So, what happens is every 5 seconds the renderTable() code is read every 5 seconds, which in turns invokes the function that actually reads the file. I tested this by making changes to the data file, and the shiny app updated without any interaction from me. Works like a charm.
If this is inelegant or is not efficient, please let me know if it can be improved, this was the most straight-forward way I could figure this out. Thanks to everyone for the help and comments!

Print/save interactive report created using Rmarkdown and Shiny

I have an interactive Rmarkdown document which embeds a few shiny apps that generates graphs based on user inputs.
After I am happy with the graphs generated, I would like to save the report (so that it becomes static).
I have opened the report in my browser (Chrome), and tried printing to pdf. It sort of works, however some figures are cut into two by the page break.
Does anyone know what is the best way to print/save such reports?
I think it's a bit tricky but this is what i use on my app to save html plot on pdf or png format.
Instal wkhtmltopdf
wkhtmltopdf and wkhtmltoimage are open source (LGPLv3) command
line tools to render HTML into PDF and various image formats using the
Qt WebKit rendering engine. These run entirely "headless" and do not
require a display or display service.
Use it in R
This allow you to convert a htmlfile into pdfor img.
In my ShinyApp i use inside a downloadHandler() function somthing like this :
system("wkhtmltopdf --enable-javascript --javascript-delay 2000 plot.html plot.pdf")
I think for your example you could simply convert your html by using :
system("wkhtmltopdf yourFile.html yourFile.pdf")
You can give a download button on the ui.R as:
downloadButton('report', 'Download PDF')
And the corresponding server.R code:
library(knitr)
output$report = downloadHandler(
filename = function() {
paste("Report_", <date/identifier>, ".pdf", sep="")
},
content = function(file) {
rnw <- normalizePath('input.rnw') # assuming you wrote the code to display the graphs etc in this file
owd <- setwd(tempdir())
on.exit(setwd(owd))
file.copy(rnw, 'input.rnw')
out = knit2pdf(rnw, clean=TRUE)
file.rename(out, file)
}
)

Download handler does not save file shiny R

library(shiny)
library(plyr)
shinyServer(function(input, output){
myfile<-reactive({
#reading 3 csv files and merging them into a dataframe
})
output$downloadData<-downloadHandler(
filename = function(){
paste("mergedfile","csv",sep='.')
},
content= function(file){
write.csv(myfile(),file,sep=",")
}
)
})
I am reading 3-4 files reactively and then merging them. After that without displaying them I need to download the merged file.
I wrote the above code but a dialog box opens which asks me where to save but the file doesn't get saved. Am I doing something wrong with the download handler.
ui.R
downloadButton('downloadData','Download')
This is what I have in my main panel in ui.R file
Your probably using the Rstudio viewer to run the app? Open the app in your browser and your code will work
( click open in borwser or run runApp('/path/to/myApp',launch.browser=T) ).
See this link.
Also no need to set sep="," for write.csv since this is the default.

Resources