Skip to content

Commit c563c71

Browse files
committed
pr: address main points of review from @gogonzo
1 parent 14cdf42 commit c563c71

File tree

3 files changed

+102
-33
lines changed

3 files changed

+102
-33
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ S3method(create_sparklines,default)
88
S3method(create_sparklines,factor)
99
S3method(create_sparklines,logical)
1010
S3method(create_sparklines,numeric)
11+
S3method(tools::toHTML,markdown_teal_internal)
1112
export(add_facet_labels)
1213
export(get_scatterplotmatrix_stats)
1314
export(tm_a_pca)

R/tm_rmarkdown.R

Lines changed: 92 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
#' req(data())
7979
#' within(data(),
8080
#' {
81-
#' params$n_rows <- n_rows_value
81+
#' n_rows <- n_rows_value
8282
#' },
8383
#' n_rows_value = input$n_rows
8484
#' )
@@ -172,27 +172,13 @@ srv_rmarkdown <- function(id, data, rmd_file, allow_download, extra_transform) {
172172
checkmate::assert_class(data, "reactive")
173173
checkmate::assert_class(isolate(data()), "teal_data")
174174
moduleServer(id, function(input, output, session) {
175-
if (allow_download) {
176-
output$download_rmd <- downloadHandler(
177-
filename = function() basename(rmd_file),
178-
content = function(file) file.copy(rmd_file, file),
179-
contentType = "text/plain"
180-
)
181-
}
182-
183175
pre_decorated_q_r <- reactive({
184176
data_q <- req(data())
185177
teal.reporter::teal_card(data_q) <- c(
186178
teal.reporter::teal_card(data_q),
187179
teal.reporter::teal_card("## Module's output(s)")
188180
)
189-
eval_code(
190-
data_q,
191-
sprintf(
192-
"params <- list(%s)",
193-
toString(sprintf("%1$s = %1$s", sapply(names(data_q), as.name)))
194-
)
195-
)
181+
data_q
196182
})
197183

198184
q_r <- data_with_output_decorated <- teal::srv_transform_teal_data(
@@ -201,6 +187,32 @@ srv_rmarkdown <- function(id, data, rmd_file, allow_download, extra_transform) {
201187
transformators = extra_transform
202188
)
203189

190+
if (allow_download) {
191+
output$download_rmd <- downloadHandler(
192+
filename = function() basename(rmd_file),
193+
content = function(file) {
194+
lines <- readLines(rmd_file)
195+
196+
# find the end of the YAML header or start of the file
197+
# and insert the contents of teal.code::get_code(q_r())
198+
yaml_end <- which(lines == "---")[2]
199+
insert_pos <- if (!is.na(yaml_end)) yaml_end else 0
200+
note_lines <- c(
201+
"",
202+
"```{r}",
203+
"# The following code chunk was automatically added by the teal markdown module",
204+
"# It shows how to generate the data used in this report",
205+
teal.code::get_code(q_r()),
206+
"```",
207+
""
208+
)
209+
lines <- append(lines, note_lines, after = insert_pos)
210+
writeLines(lines, con = file)
211+
},
212+
contentType = "text/plain"
213+
)
214+
}
215+
204216
temp_dir <- tempdir()
205217
temp_rmd <- tempfile(tmpdir = temp_dir, fileext = ".Rmd")
206218
file.copy(rmd_file, temp_rmd) # Use a copy of the Rmd file to avoid modifying the original
@@ -212,12 +224,11 @@ srv_rmarkdown <- function(id, data, rmd_file, allow_download, extra_transform) {
212224
rmarkdown::render(
213225
temp_rmd,
214226
output_format = rmarkdown::md_document(
215-
variant = "gfm",
216-
toc = TRUE,
217-
preserve_yaml = TRUE
227+
variant = "markdown",
228+
standalone = TRUE,
229+
dev = "png"
218230
),
219-
params = datasets[["params"]],
220-
envir = new.env(parent = globalenv()),
231+
envir = environment(datasets),
221232
quiet = TRUE,
222233
runtime = "static"
223234
)
@@ -254,11 +265,69 @@ srv_rmarkdown <- function(id, data, rmd_file, allow_download, extra_transform) {
254265
out_data@verified <- FALSE # manual change verified status as code is being injected
255266
}
256267

268+
report_doc <- .markdown_internal(rendered_path_r(), temp_dir, rendered_html_r())
257269
teal.reporter::teal_card(out_data) <- c(
258-
teal.reporter::teal_card(out_data),
259-
rendered_html_r()
270+
teal.reporter::teal_card(out_data), report_doc
260271
)
261272
out_data
262273
})
263274
})
264275
}
276+
277+
#' @exportS3Method tools::toHTML
278+
toHTML.markdown_teal_internal <- function(block, ...) {
279+
cached_html <- attr(block, "cached_html", exact = TRUE)
280+
if (!is.null(cached_html)) {
281+
return(cached_html)
282+
}
283+
NextMethod(unclass(block), ...)
284+
}
285+
286+
#' @method to_rmd markdown_internal
287+
to_rmd.markdown_teal_internal <- function(block, figures_dir = "figures", include_chunk_output = TRUE, ...) {
288+
images_base64 <- attr(block, "images_base64", exact = TRUE)
289+
for (img_path in names(images_base64)) {
290+
img_data <- sub("^data:.*;base64,", "", images_base64[[img_path]])
291+
img_tag_pattern <- paste0("!\\[.*?\\]\\(", img_path, "\\)")
292+
dir.create(figures_dir, showWarnings = FALSE, recursive = TRUE)
293+
path <- file.path(
294+
figures_dir,
295+
sprintf(
296+
"markdown_img_%s.%s",
297+
substr(rlang::hash(img_data), 1, 6),
298+
sprintf("%s", tools::file_ext(img_path))
299+
)
300+
)
301+
writeBin(base64enc::base64decode(img_data), path)
302+
replacement_tag <- sprintf("![](%s)", path)
303+
block <- gsub(img_tag_pattern, replacement_tag, block, fixed = FALSE)
304+
}
305+
NextMethod(unclass(block), ...)
306+
}
307+
308+
.markdown_internal <- function(markdown_file, temp_dir, rendered_html) {
309+
# Read the markdown file
310+
lines <- readLines(markdown_file)
311+
images_base64 <- list()
312+
313+
# Extract images based on pattern ![](.*)
314+
img_pattern <- "!\\[.*?\\]\\((.*?)\\)"
315+
img_tags <- unlist(regmatches(lines, gregexpr(img_pattern, lines)))
316+
for (ix in seq_along(img_tags)) {
317+
img_tag <- img_tags[[ix]]
318+
img_path <- gsub("!\\[.*?\\]\\((.*?)\\)", "\\1", img_tag)
319+
full_img_path <- file.path(temp_dir, img_path)
320+
if (file.exists(full_img_path)) {
321+
img_data <- knitr::image_uri(full_img_path)
322+
images_base64[[img_path]] <- img_data
323+
}
324+
}
325+
326+
# Create new custom structure with contents and images in base64 as attribute
327+
structure(
328+
lines,
329+
class = c("markdown_teal_internal", "character"),
330+
images_base64 = images_base64,
331+
cached_html = rendered_html
332+
)
333+
}

inst/sample_files/test.Rmd

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
---
22
title: "Test R Markdown"
33
output: html_document
4-
params:
5-
CO2: NULL
6-
n_rows: NULL
74
---
85

6+
```{r, eval=!exists(".raw_data"), include=FALSE}
7+
# Set your local data here. note that when used in teal the `data` must be available to the module
8+
CO2 <- datasets::CO2
9+
n_rows <- Inf
10+
```
11+
912
This is an example of an R markdown file with an inline r execution that gives the current date: `r Sys.Date()`
1013

1114
Code chunk that performs a simple calculation (`1+1`)
@@ -16,18 +19,14 @@ Code chunk that performs a simple calculation (`1+1`)
1619

1720
Code chunk that shows the structure of the params object
1821

19-
```{r}
20-
lapply(params, class)
21-
```
22-
2322
Code chunk that shows the summary of the first `n_rows` of the `CO2` dataset if it is provided
2423

2524
```{r}
26-
summary(head(params$CO2, n = params$n_rows))
25+
summary(head(CO2, n = n_rows))
2726
```
2827

2928
Code chunk that plots the first `n_rows` of the `CO2` dataset if it is provided
3029

31-
```{r, eval = !is.null(params$CO2)}
32-
plot(head(params$CO2, n = params$n_rows))
30+
```{r}
31+
plot(head(CO2, n = n_rows))
3332
```

0 commit comments

Comments
 (0)