I want to write Rmarkdown code with Rmarkdown

214 views Asked by At

I ran into the following problem. In order to automatically generate problem sets with Rexams, I want to read questions from a database and then parse them into a Rexams template and then produce another markdown file, that can potentially contain chunks of R code, that then should be processed by Rexams. I tried several forms of getting verbatim text such as

write_chunk1 <- c("```{r, echo = FALSE, results='hide'} \n " )
write_chunk2 <- c("   include_supplement('UCL-J16-Q13-01.png', recursive = TRUE) \n" )
write_chunk3 <- c("```")

cat(noquote(write_chunk1) )
cat(noquote(write_chunk2) )
cat(noquote(write_chunk3) )

but this does not produce the required output in the generated markdown file. Which would be the following (I escaped the last code closing, otherwise it would not show up here either)

```{r, echo = FALSE, results='hide'} 
include_supplement('UCL-J16-Q13-01.png', recursive = TRUE) 
```

The following file runs a loop that takes the "quizzes" database puts the information into an Rexams template

library(knitr)
library(rmarkdown)
library(here)
library(exams)
library(dplyr)
library(readxl)



## paths
# file.copy(here("quizzes/input/images","*.png"),to = here("quizzes/output/rmd_question_files/"))

## Data


quizzes <- tribble(
~identifier,  ~question, ~answer1, ~answer2, ~solution1, ~solution2, ~feedback1, ~feedback2, ~image, 
 "Q1", "Is this good",   "yes", "no", 1, 0, "Because it is", "Wrong", "smile.png"
  )


## Loop
for (i in 1:nrow(quizzes)){
  rmarkdown::render(input = here("quizzes/code/", "template_rexams.Rmd"),
                    output_format = "md_document",
                    output_file = paste0(quizzes$Identifier[i], ".Rmd"),
                    output_dir = here("quizzes/output/rmd_question_files/")
  )
}

The rexams template serves as a template for the markdown files that shousl contain the different exercises.

---
output: md_document
---
 
```{r echo=FALSE, message=FALSE, warning=FALSE}


quizzes <- tribble(
  ~question, ~answer1, ~answer2, ~solution1, ~solution2, ~feedback1, ~feedback2, ~image, 
  "Is this good",   "yes", "no", 1, 0, "Because it is", "Wrong", "smile.png"
  )

# Read question
question <- quizzes$question[i]

# Create answer lists and solutions with feeback
answers <- c(quizzes$answer1[i], quizzes$answer2[i])

sol <- c(quizzes$solution1[i], quizzes$solution2[i])
sol <- ifelse(sol == 1, TRUE, FALSE)

feedback <- c(quizzes$feedback1[i], quizzes$feedback2[i])

## Get image identifiers
img1 <- quizzes$image

```


```{r get-images, echo=FALSE, message=FALSE, warning=FALSE}
# Define variable containing image locations
img_path <- here::here("quizzes/input/images/")

img1_pt <- ifelse(img1 != "", paste0(img_path, paste0(quizzes$image[i],".png")), "")

img1 <- ifelse(img1 != "", paste0(quizzes$image,".png"), "")


if (!is.na(img1)){
  include_supplement(file = c(img1), dir = img_path, recursive = TRUE)
}

write_chunk1 <- c("{r, echo = FALSE, results='hide'} \n " )
write_chunk2 <- c("   include_supplement('")
write_chunk3 <- c(paste0(img1, ", recursive = TRUE) \n" ))
write_chunk4 <- c("```")

```

```{r write_supplement, echo=FALSE, message=FALSE, warning=FALSE, results="asis"}
cat(noquote(write_chunk1) )
cat(noquote(write_chunk2), noquote(write_chunk3) )
cat(noquote(write_chunk4) )
```


Question
========
**Unit `r unit`.`r nmb`**

`r question`


```{r insert-images, echo=FALSE, message=FALSE, warning=FALSE, comment="", results="asis"}

img1_pr <- ifelse(img1 != "", paste0("![](", paste0(img1, ")")), " ")


```

`r if(!is.na(img1)){noquote(img1_pr)}`


```{r answerlist, echo = FALSE, results = "asis"}
answerlist(answers, markup = "markdown")
```


Solution
========

```{r solutionlist, echo = FALSE, results = "asis"}
answerlist(ifelse(sol == 1, "**True**", "**False**"), markup = "markdown", feedback)
```

Meta-information
================

extype: mchoice 

exsolution: `r mchoice2string(sol, single = FALSE)` 

exname: `r exname` 
      

The problem now is, that this new markdown file should contain the following code chunk at the beginning of the markdown file, where the name of the image to be included changes by exercise.

```{r, echo = FALSE, results='hide'} 
include_supplement('smile.png', recursive = TRUE) 
```

I hope this explains a bit better my problem. I think my problem is that I do not know how to escape ``` in order to write it into the file.

1

There are 1 answers

0
Achim Zeileis On BEST ANSWER

There is no unified function for this because the exact representation of the questions across data frames, CSV or database files, typically various. However, the general approach I use is to set up a character template for the Rmd (or Rnw) exercise with %s string placeholders and the fill the placeholders with the information from the data. Eventually, I use writeLines() to write out the resuling code to the exercise file.

A worked example for processing a certain CSV file format is discussed in the R/exams forum on R-Forge at: https://R-Forge.R-project.org/forum/forum.php?thread_id=34046&forum_id=4377&group_id=1337

I adapted the code from that post into a data2rmd() function that can be applied to the quizzes tibble from your question. It assumes that there are always exactly two answer options and then you can do:

data2rmd(quizzes)

which creates a file ex1.Rmd with the R/Markdown code. This can then be processed with exams2html("ex1.Rmd") or any other exams2xyz() function - provided that the image file smile.png is in the same folder as ex1.Rmd.

Function:

data2rmd <- function(x, ...) {
  ## Rmd exercise template
  rmd <- '
Question
========

%s
%s

Answerlist
----------

* %s
* %s

Solution
========

Answerlist
----------

* %s
* %s

Meta-information
================
exname: %s
extype: schoice
exsolution: %s
'

  ## convenience functions
  include_image <- function(x) {
    if(x == "") return("")  
    rmd <- '```{r, echo = FALSE, results = "hide"}
include_supplement("%s")
```
\\
![](%s)'
    sprintf(rmd, x, x)
  }
  
  ## insert data base into template
  nam <- paste0("ex", 1L:nrow(x))
  rmd <- sprintf(rmd,
    x$question,
    sapply(x$image, include_image),
    x$answer1, x$answer2,
    x$feedback1, x$feedback2,
    nam,
    paste0(x$solution1, x$solution2)
  )


  ## write Rmd files
  for(i in 1L:nrow(x)) writeLines(rmd[i], paste0(nam[i], ".Rmd"))
  invisible(rmd)
}