I am interested in feeding a varying number of answers taken as arguments for a learnr
quiz question.
Like, for instance, building the 'body' of a question_checkbox
from external lists of questions and answers (like taken from a csv).
In the example provided in the learnr
tutorial, the question is built manually. I want to understand how to do it dynamically.
question_checkbox(
"Select all the toppings that belong on a Margherita Pizza:",
answer("mozzarella", correct = TRUE),
answer("basil", correct = TRUE),
answer("onions"),
answer("spinach"),
random_answer_order = TRUE,
allow_retry = TRUE,
try_again = "Be sure to select all toppings!"
)
I tried feeding the answers as a list, which is prevented by the answer's specific class ("tutorial_question_answers" "tutorial quiz answer"
).
I tried a workaround using r/exams which appears to allow embedding r/exams
quiz files and uses the exams2learnr
function to convert them tolearnr
exercises, but for some reason it does not work.
The only question on StackOverflow that is relatively close is Is there a way to import questions and answers from a spreadsheet into a learnr quiz? but it is about dynamically extracting data from external sources and using them as (a fixed number of) arguments, not about dynamically changing the number of arguments itself (e.g., the number of possible answers). It takes me half there but not all the way (I'll keep trying to modify it myself in the meantime).
One option to achieve your desired result would be to use do.call
.
Not sure about the structure of your input data. Hence, based on the example from the docs I first created two datasets, one containing the data on the questions, one containing the answers. As a first step I join the two datasets where I put the answers data in a list column (via tidyr::nest
).
After this preliminary steps I use purrr::pmap
to loop over the questions (even if the example contains only one). For each question I use a second pmap
step to dynamically create the list of answer()
s which are then be passed to question_checkbox
via do.call
.
---
title: "Untitled"
output: learnr::tutorial
runtime: shiny_prerendered
date: "2023-10-18"
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```
```{r include=FALSE}
library(dplyr)
library(tidyr)
library(purrr)
library(learnr)
```
```{r include=FALSE}
questions <- tibble::tibble(
qid = "q01",
text = "Select all the toppings that belong on a Margherita Pizza:",
random_answer_order = TRUE,
allow_retry = TRUE,
try_again = "Be sure to select all four toppings!"
)
answers <- tibble::tibble(
qid = "q01",
text = c(
"tomato", "mozzarella", "basil",
"pepperoni", "extra virgin olive oil",
"onions", "bacon", "spinach"
),
correct = c(rep(TRUE, 4), rep(FALSE, 4)),
message = c(
rep(NA, 3),
"Great topping! ... just not on a Margherita Pizza",
rep(NA, 4)
)
)
answers <- answers |>
tidyr::nest(answers = -qid)
questions_answers <- questions |>
left_join(answers, by = "qid")
```
```{r}
glimpse(questions_answers)
```
```{r create-questions, include=FALSE}
create_answers <- function(...) {
args <- list(...)
args$message <- if (!is.na(args$message)) args$message
do.call(learnr::answer, args)
}
q_checkbox <- purrr::pmap(
questions_answers,
function(...) {
args <- list(...)
args$qid <- NULL
answers <- purrr::pmap(args$answers, create_answers)
do.call(
learnr::question_checkbox,
c(args[!names(args) == "answers"], answers)
)
}
)
```
```{r q01}
q_checkbox[[1]]
```