R Shiny downloadHandler does not work inside flexdashboard - r

I am working in a R Shiny Flexdashboard, and including a download button. To distribute the items on the screen I would like to use splitLayout. But the issue is, if I use downloadButton inside the SplitLayout, the downloadHandler throws a very strange behaviour... in RStudio, it tries to save an (apparently) empty .Rmd file. In the browser, it tries to save an (apparently) empty .htm file...
The code with a minimal example is the following:
splitLayout(cellWidths = c("30%", "70%"),
wellPanel(
downloadButton("downloadData")
),
dataTableOutput("OrigData")
)
output$downloadData<-downloadHandler(
filename = "OriginalData.csv" ,
content = function(file) {
write.csv(mtcars, file=file)
},
contentType="text/csv"
)
output$OrigData<-DT::renderDataTable(mtcars)
I would like to understand what is going on...
From the link apparently downloadHandler works just if one put it just after the download button code (also just if the last one wrapped inside a wellPanel)... But the code works well if isolated just this part.
Other parts of app are still just text/Markdown, and column and tab headers because it is in the draft state...
What is happening when the downloadHandler tries to save a .Rmd and/or html file?

This worked for me:
---
title: "app"
output:
flexdashboard::flex_dashboard:
orientation: columns
vertical_layout: fill
runtime: shiny
---
```{r}
library(flexdashboard)
library(shiny)
library(DT)
splitLayout(cellWidths = c("30%", "70%"),
wellPanel(
downloadButton("downloadData")
),
dataTableOutput("OrigData")
)
output$downloadData<-downloadHandler(
filename = "OriginalData.csv" ,
content = function(file) {
write.csv(mtcars, file=file)
},
contentType="text/csv"
)
output$OrigData <- renderDataTable(mtcars)
```

I am including an answer because I tried to answer the comments and it disappeared...I believe I figured out the answer but it would be an intriguing flexdashboard+downloadHandler behaviour...
As mentioned by #Phil, the isolated code I posted here indeed works, I tried now (sorry for not doing it before)...
I went back to my original code and isolated the corresponding code (with my data). It also works (it downloads correctly the .csv)
The remaining codes were theoretically irrelevant for the problem... including just the remaining code keep the downloadhandler working fine...
The problem appears to be the titles (column, tabs titles - .data-navmenu, .storyboard)... It makes little sense, are them just titles… But I modified the original titles (there were some similar titles) and it also solved the issue… the app stopped trying to save .Rmd or .htm, and started to enable to download .csv…
I believe the downloadHandler() does not work when there are similar column/tab titles in the flexdashboard... Does it make sense? It sounds a bit strange to be the answer for this but worked...

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.

Sweave, Shiny : can't generate a PDF on the server

I'm working on R Sweave to generate a report in PDF in a shiny application on a server. Everything works perfectly in my computer, locally, I can generate my PDF in the application. It also works in the server. But, when I wanted to add an image in a background, a rectangular header, in the R Sweave , it didn't work anymore on the server, it can't be generated.
Here's the code I added in my R Sweave, so my report :
\usepackage{background}
\usepackage{graphicx}
\backgroundsetup{
scale=0.5,
angle=0,
opacity=1,
color=black,
contents={\begin{tikzpicture}[remember picture, overlay]
\node at ([yshift=-.6in] current page.north)
{\includegraphics[width = \paperwidth]{myheader}};
\end{tikzpicture}}
}
Did I forget something ?
Thank you
EDIT : After some researches, I think I probably have a problem with these functions in the script server.R :
output$report <- downloadHandler(
filename = function(){name()},
content = function(file) {
out = knitr::knit2pdf(input="my_report.Rnw",encoding = "UTF-8",clean=TRUE)
file.rename(out, file)
file.copy(file,paste0("export/",Sys.Date(),"_",name()))
},
contentType = 'application/pdf'
)
It seems that I probably forgot an argument in my function out = knitr::knit2pdf(input="my_report.Rnw",encoding = "UTF-8",clean=TRUE). It seems that it can only manage a text on UTF-8 and geometric forms, not an image.
Problem solved! In my R Sweave, I just added \usepackage{tikz} and it works!
Thanks to Dirk Eddelbuettel for his help.

How do I embed tutorial questions from 'learnr' into a full shiny app?

I am trying to embed a tutorial Rmd from the learnr package into a full shiny app. However, learnr uses the shiny_prerendered runtime, I cannot call it within my app. How do I get an interactive tutorial to run within my shiny app?
I have have three files right now: ui.R, server.R, and tutorial.Rmd.
My tutorial looks like this (one ` removed for formatting)
---
title: "my tutorial"
tutorial:
id: "com.example.tutorials.a-tutorial"
version: 1.0
output: learnr::tutorial
runtime: shiny_prerendered
---
``{r setup, include=FALSE}
library(learnr)
knitr::opts_chunk$set(echo = FALSE)
``
### Exercise Example
An R code question
``{r add-function, exercise=TRUE, exercise.lines = 5}
add <- function() {
}
``
### Quiz
R Quiz Question
``{r quiz}
quiz(
question("Question 1",
answer("wrong"),
answer("also wrong"),
answer("right", correct = TRUE),
answer("wrong again")
)
)
``
When I try rendering the output of this file from ui.R like so:
ui <- tagList(
fluidPage(theme = shinytheme("cosmo")),
navbarPage(
"appTitle",
tabPanel("Embedding Tutorials?",
includeMarkdown("tutorial.Rmd")
),
)
)
It (properly, I believe) displays it as a regular old Rmd file, not an interactive tutorial.
I've also tried using rmarkdown::render("tutorial.Rmd") which just renders the filepath to the html file generated by the Rmd (/Users/me/app/tutorial.html).
When I try to render any tutorial using run_tutorial("hello", package="learnr"), it (again, rightfully) gives the error
ERROR: Can't callrunApp()from withinrunApp(). If your application code containsrunApp(), please remove it.
I've already discovered that I can create question chunks using the question() function in learnr using the following:
ui <- tagList(
fluidPage(theme = shinytheme("cosmo")),
navbarPage(
"appTitle",
tabPanel("Tutorial",
quiz(
question("Quiz question",
answer("1"),
answer("2"),
answer("3", correct = TRUE),
answer("4"),
allow_retry = TRUE
)
),
)
)
But this does not allow the functionality of creating R code chunks that can be run within the app.
What I want is a fully interactive learnr tutorial that can be rendered from within a ui.R file for a shiny app. Is this possible?
As well as my suggestion to incorporate your extra material into the learnr tutorial I also got <iframe> embedding to work. Create an app.R with the following contents:
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("learnr tutorial"),
# Show a plot of the generated distribution
mainPanel(fluidRow(
htmlOutput("frame")
))
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$frame <- renderUI({
tags$iframe(
src="https://jjallaire.shinyapps.io/learnr-tutorial-03a-data-manip-filter/", width=1280, height=720
)
})
}
# Run the application
shinyApp(ui = ui, server = server)
Now when you Run App this should embed the example tutorial from https://rstudio.github.io/learnr/
It appears to be necessary for the tutorial to be rendered and published to shinyapps.io, etc.: I couldn't get it to work just from the rendered html file. So,
Create tutorial
Publish tutorial
Embed tutorial
seems to be the way forward.
Generally speaking, there are two ways to embed interactive RMarkdown documents in shiny applications.
(1) The usual way (as proposed by #Phil) is to have one R server running the embedded tutorial and another one running the application. This can be archived by deploying the tutorial via shinyapps.io or shiny-server first and then using an iframe. Alternatively, you could use callr::r_bg() to run the tutorial in a local background process. In any case, this will make it so the Rmd document can not interact with the shiny application. If this is feasable for your usecase, I would suggest this option.
(2) A more convoluted way is outlined here by the maintainer of the shinyAce package. It uses the same R server for the main application and the embedded Rmd document. See also this SO question. AFAIK, this only works with knitr::knit2html which relies on an outdated version of RMarkdown. Also, the amount of render* and output* functions available in this manner is limited unless you make sure certain JavaScript and CSS resources are properly included in your ui definition.
Quite some time has passed since I wrapped my head around this topic but my impression at the time was, that (2) takes quite a lot of work and really limits your options.

shiny mainPanel width when including markdown

I'm trying to include a "reactive" .Rmd file within a shiny application. It would summarize the user's choices and various conditions that arise. For whatever reason, I find including shiny within .Rmd documented, but not including the ability to knit .Rmd and have the output included inside a shiny app.
I find these very hard to reproduce, so here's my attempt using the hello world example from Rstudio. Here's the app as-is in chromium:
Next, I add a uiOutput element to ui.R:
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot"),
uiOutput("mark")
)
I also add a corresponding output in server.R, taken from this sort of similar question:
output$mark <- renderUI({
HTML(markdown::markdownToHTML(knit("./test.Rmd", quiet = TRUE)))
})
The contents of test.Rmd are this:
## something to print
```{r}
head(mtcars, input$bins)
```
When I run it, I now get this (screenshot footprint is the same):
I'm assuming there's some css or something in the knit document that's goofing things up, but I don't know. I can force the widths wider, but the main panel stays sort of centered.
I've tried includeMarkdown but wasn't able to get it to update based on input. Keep in mind that's the eventual hope -- I need something that can regenerate whatever is displayed based on changed input values. I realize I could do renderText or something, but the Rmarkdown format is so convenient as this is sort of a guide. I can write some documentation, then show the user what they've selected and what happens as a result.
It seems close... I just can't figure out why the page goes wonky.
I also tried stuff like style = "width: 1200px" or style = "width: 100%" without being able to get the ratio between the sidebarPanel and mainPanel back to normal.
Ideally, the output would look just like the original, but the knit results would show up under the current footprint of the plot.
It appears that the rmd code adds the following property to the body element:
max-width: 800px;
To remove this, add the following to your ui code (for example below sliderInput)
tags$head(tags$style(HTML("
body {
width: 100% !important;
max-width: 100% !important;
}
")))
Set fragment.only=TRUE in markdownToHTML:
output$mark <- renderUI({
HTML(markdown::markdownToHTML(knit("./test.Rmd", quiet = TRUE), fragment.only=TRUE))
})

rmarkdown shiny user input in chunk?

I have a shiny app that allows the user to download an HTML file (knitted from a .Rmd file) that includes the code used to run the analysis based on all the user inputs. I am trying to write the base .Rmd file that gets altered when user inputs vary. I am having trouble including user input variables (e.g. input$button1) into R code chunks. Say the user input for input$button1 = "text1".
```{r}
results <- someFun(input$button1)
```
And I'd like to have it knitted like this:
```{r}
results <- someFun('text1')
```
Every time I download the knitted HTML though, I get input$button1 getting written to file. I would also like to be able to produce an .Rmd file that is formatted with this substitution. It seems like knit_expand() might be the key, but I can't seem to relate available examples to my specific problem. Is the proper way to knit_expand() the whole .Rmd file and specify explicitly all the parameters you want subbed in, or is there a more elegant way within the .Rmd file itself? I would prefer a method similar to this, except that instead of using the asis engine, I could use the r one. Any help would be greatly appreciated. Thanks!
Got it. Solution below. Thanks to Yihui for the guidance. The trick was to knit_expand() the whole .Rmd file, then writeLines() to a new one, then render. With hindsight, the whole process makes sense. With hindsight.
For the example, p1 is a character param 'ice cream' and p2 is an integer param 10. There is a user-defined param in ui.R called input$mdType that is used to decide on the format provided for download.
Rmd file:
Some other text.
```{r}
results <- someFun("{{p1}}", {{p2}})
```
in the downloadHandler() within server.R:
content = function(file) {
src <- normalizePath('userReport.Rmd')
# temporarily switch to the temp dir, in case you do not have write
# permission to the current working directory
owd <- setwd(tempdir())
on.exit(setwd(owd))
file.copy(src, 'userReport.Rmd')
exp <- knit_expand('userReport.Rmd', p1=input$p1, p2=input$p2)
writeLines(exp, 'userReport2.Rmd')
out <- rmarkdown::render('userReport2.Rmd', switch(input$mdType,
PDF = pdf_document(), HTML = html_document(), Word = word_document()))
}
file.rename(out, file)
}
Resulting userReport2.Rmd before rendering:
```{r}
results <- someFun("ice cream", 10)
```

Resources