Nothing
testvision2exams <- function(x, markup = c("markdown", "latex"), rawHTML = FALSE,
dir = ".", exshuffle = TRUE, name = NULL, shareStats = FALSE)
{
## read Moodle XML file (if necessary)
stopifnot(requireNamespace("xml2"))
if(!inherits(x, "xml_node") && length(x) == 1L) x <- xml2::read_xml(x)
## set up template in indicated markup
markup <- match.arg(markup)
if(markup == "markdown") markup <- "markdown_strict"
if(markup == "latex" & rawHTML) rawHTML <- FALSE #raw html is only relevant for markdown markup
if(!(markup %in% c("markdown_strict", "latex"))) stop("'markup' must be either markdown/markdown_strict or latex")
fext <- if(markup == "markdown_strict") "Rmd" else "Rnw"
tmpl <- if(markup == "markdown_strict") {
'Question
========
%s
%s
%s
Meta-information
================
exname: %s
extype: %s
exsolution: %s
%s
'
} else {
'\\begin{question}
%s
%s
\\end{question}
%s
%%%% Meta-information
\\exname{%s}
\\extype{%s}
\\exsolution{%s}
%s
'
}
## convenience functions
answerlist_env <- function(x) {
if(is.list(x)) {
x <- lapply(x, paste, collapse = "\n ")
x <- unlist(x)
}
if(markup == "markdown_strict") {
c("Answerlist", "----------", paste("*", x))
} else {
c("\\begin{answerlist}", paste(" \\item", x), "\\end{answerlist}")
}
}
solution_env <- function(solutions, feedback) {
if(length(solutions) > 0L) solutions <- answerlist_env(solutions)
if(any(grepl("answerlist", tolower(feedback)))) {
solutions <- feedback
feedback <- NULL
}
solutions <- c(feedback, solutions)
solutions <- solutions[!grepl("tightlist", solutions)]
solutions <- if(markup == "markdown_strict") {
c("Solution", "========", solutions)
} else {
c("\\begin{solution}", solutions, "\\end{solution}")
}
}
name_to_file <- function(name) {
name <- gsub(":|,|!", "", name)
name <- gsub(" ", "_", name)
for(i in c("...", "|", "/", "(", ")", "[", "]", "{", "}", "<", ">")) {
name <- gsub(i, "", name, fixed = TRUE)
}
return(name)
}
stripdiv <- function(xml) { #remove the <div> that TVO puts everywhere
xml <- gsub("</div>", "", xml)
xml <- gsub("<div [^>]*>", "", xml)
xml <- gsub("\\n", " ", xml)
xml <- gsub("(?<=[\\s])\\s*|^\\s+|\\s+$", "", xml, perl = TRUE)
xml
}
stripmodf <- function(xml) { #remove the modalFeedback environment
xml <- stripdiv(xml)
xml <- gsub("</modalFeedback>", "", xml)
xml <- gsub("<modalFeedback [^>]*>", "", xml)
xml <- gsub("\\n", "", xml)
xml <- gsub("(?<=[\\s])\\s*|^\\s+|\\s+$", "", xml, perl = TRUE)
}
exfile <- function(x) {
if(any(grepl('<a href', x))) {
fls <- regmatches(x, gregexpr('(?<=href=")(.|\n)*?(?=")', x, perl = TRUE))[[1L]]
tgs <- regmatches(x, gregexpr('<a href="[^>]*>((.|\n)*)</a>', x))
for(i in 1 : length(fls)) {
x <- gsub(tgs[i], paste0("[", fls[i], "](", fls[i], ")"), x)
}
}
return(x)
}
equation <- function(x) {
if(any(grepl('application/x-tex', x))) {
eqs <- regmatches(x,gregexpr('(?<=<math)(.|\n)*?(?=</math>)', x, perl = TRUE))[[1L]]
eqs <- as.vector(apply(as.matrix(eqs), 1, function(x) paste0("<math", x, "</math>")))
inline <- grepl('display="inline', eqs)
aligned <- grepl('(\\{aligned|\\{eqnarray)', eqs)
for(i in 1:length(eqs)) {
tmp <- regmatches(eqs[i], gregexpr('<math[^>]*>(.*)x-tex">', eqs[i]))[[1]]
x <- sub(tmp, if(inline[i]) "\\(" else if(!aligned[i]) "\\[" else "", x, fixed = TRUE)
x <- sub("</annotation></semantics></math>", if(inline[i]) "\\)" else if(!aligned[i]) "\\]" else "", x, fixed = TRUE)
}
x <- gsub("#38;", "", x)
x <- gsub("\\\\begin\\{aligned\\}", "<br>\\\\begin\\{aligned\\}<br>", x)
x <- gsub("\\\\end\\{aligned\\}", ",<br>\\\\end\\{aligned\\}<br>", x)
x <- gsub("\\\\begin\\{eqnarray\\*\\}", "<br>\\\\begin\\{eqnarray\\*\\}<br>", x)
x <- gsub("\\\\end\\{eqnarray\\*\\}", "<br>\\\\end\\{eqnarray\\*\\}<br>", x)
x <- gsub("\\\\\\\\", "\\\\\\\\<br>", x)
}
return(x)
}
mediafile <- function(x) {
supps <- NULL
if(length(i <- grep("img src=", x, fixed = TRUE))) {
for(j in i) {
img <- x[j]
fpath <- regmatches(img, gregexpr('(?<=img src=").*?(?=")', img, perl = TRUE))[[1L]]
for(k in 1 : length(fpath)){
fname <- name_to_file(basename(fpath[k]))
file.copy(paste0("./", fpath[k]), file.path(dir, fname))
x <- gsub(fpath[k], fname, x, fixed = TRUE)
supps <- c(supps, fname)
}
}
}
if(length(i <- grep('href=\"mediafiles', x, fixed = TRUE))) {
for(j in i) {
df <- x[j]
dfl <- regmatches(df, gregexpr('<a href="[^>]*>(.*)</a>', df))
fpath <- regmatches(dfl, gregexpr('(?<=href=").*?(?=")', dfl, perl = TRUE))[[1L]]
for(k in 1 : length(fpath)){
fname <- name_to_file(basename(fpath[k]))
file.copy(paste0("./", fpath[k]), file.path(dir, fname))
x <- gsub(fpath[k], fname, x, fixed = TRUE)
x <- gsub(paste0("<code>", fname, "</code>"), "", x, fixed = TRUE)
supps <- c(supps, fname)
}
}
}
return(list("txt" = x, "supplements" = supps))
}
## Collect supplements.
supps <- NULL
## get basic parts from question
x <- xml2::xml_ns_strip(x)
exname <- if(is.null(name)) xml2::xml_attr(x, "title") else name
response <- xml2::xml_find_all(x, "responseDeclaration")
cardinality <- xml2::xml_attr(response,"cardinality")
baseType <- xml2::xml_attr(response,"baseType")
if(is.na(baseType)){
extype <- "cloze"
} else if(baseType == "identifier") {
if(cardinality == "single") extype <- "schoice"
else extype <- "mchoice"
} else if(baseType == "float") {
extype <- "num"
} else {
extype <- "string"
}
if(is.na(baseType)) stop("The xml-file contains no question of supported extype")
ibody <- xml2::xml_find_all(x, "itemBody")
#ibody <- xml2::xml_contents(xml2::xml_find_all(x, ".//itemBody"))
interact <- xml2::xml_find_all(ibody, switch(extype, schoice = "choiceInteraction", mchoice = "choiceInteraction",
num = "extendedTextInteraction", string = "extendedTextInteraction"))
rblock <- xml2::xml_find_all(ibody, "rubricBlock")
exshuffle <- if(!rawHTML) as.character(exshuffle) else tolower(exshuffle)
choices <- xml2::xml_children(xml2::xml_find_all(interact, "simpleChoice"))#Naar schoice en mchoice
choiceid <- lapply(xml2::xml_find_all(interact, "simpleChoice"), function(x) xml2::xml_attr(x, "identifier"))
fdbck <- xml2::xml_find_all(x, "modalFeedback")
## Remove superfluous stuff from ibody
xml2::xml_remove(interact)#
xml2::xml_remove(rblock)
cresp <- xml2::xml_text(xml2::xml_children(xml2::xml_children(response)))
## set up variables for question
exrc <- vector(mode = "list", length = 1L)
## cycle through question
## cloze not fully supported yet
if(extype == "cloze") warning("cloze conversion not fully supported yet!")
if(grep("itemBody", ibody)) {
qu <- stripdiv(xml2::xml_contents(ibody))
pff <- mediafile(qu)
supps <- c(supps, pff$supplements)
qtext <- paste0(as.character(pff$txt), collapse = "")
if(!rawHTML){
qtext <- pandoc(equation(qtext),
from = "html+tex_math_dollars+tex_math_single_backslash",
to = markup)
}
exsol <- extol <- feedback <- taxstr <- exother <- NULL
answers <- solutions <- list()
## num
if(extype == "num") {
exsol <- as.numeric(cresp[1])
if(is.na(cresp[2])) extol <- 0 else {
interval <- as.numeric(unlist(strsplit(cresp[2], ";", 2)))
extol <- abs(interval[1] - interval[2])/2
}
}
## schoice/mchoice
if(grepl("choice", extype)) {
pff <- mediafile(choices)
supps <- c(supps, pff$supplements)
ans <- lapply(pff$txt, function(x) paste0(stripdiv(x), collapse = ""))
sol <- choiceid %in% cresp
exsol <- paste0(sol*1, collapse = "")
for(j in 1L:length(ans)) {
answers[[j]] <- if(!rawHTML) pandoc(equation(ans[j]),
from = "html+tex_math_dollars+tex_math_single_backslash",
to = markup) else ans[j]
}
}
## string
if(extype == "string") {
ans <- "answer"
exsol <- "solution"
}
## feedback
if(length(fdbck)) {
xml <- fdbck
fbnames <-xml2:: xml_attr(xml, "identifier")
fbanswers <- xml2::xml_contents(xml[grep("alt_", fbnames)])
tmp <- mediafile(xml)
fdbck <- lapply(tmp$txt, stripmodf)
if(!rawHTML) fdbck <- lapply(fdbck, equation)
supps <- c(supps, tmp$supplements)
fbcorrect <- fdbck[fbnames == "ANSWER_CORRECT"]
fbfailure <- fdbck[fbnames == "FAILURE"]
fbquestion <- fdbck[fbnames == "QUESTION_FEEDBACK"]
fbanswers <- fdbck[grep("alt_", fbnames)]
## remove feedback duplicates
ufb <- unique(c(fbcorrect, fbfailure, fbquestion))
if(length(ufb) < 3) {
if(length(ufb) == 1){
fbquestion <- ufb
fbfailure <- fbcorrect <- NULL
} else {
if(identical(fbfailure, fbcorrect)) fbfailure <- NULL else {
if(identical(fbquestion, fbcorrect)) fbcorrect <- NULL else {
if(identical(fbquestion, fbfailure)) fbfailure <- NULL
}
}
}
}
if(shareStats) {
fbsubject <- NULL
taxstr <- stripdiv(unlist(fdbck[fbnames == "SUBJECT_MATTER"]))
} else fbsubject <- fdbck[fbnames == "SUBJECT_MATTER"]
fdbck <- list(fbcorrect = fbcorrect, fbfailure = fbfailure, fbquestion = fbquestion,
fbanswers = fbanswers, fbsubject = fbsubject)
fdbck <- lapply(fdbck, function(x) if(length(x) == 0) x <- NULL else x)
if(!is.null(unlist(fbanswers))){
solutions <- fbanswers
fdbck$fbanswers <- NULL
}
for(i in 1 : length(fdbck)) {
fbtmp <- fdbck[[i]]
if(!rawHTML){
fbtmp <- pandoc(fbtmp,
from = "html+tex_math_dollars+tex_math_single_backslash",
to = markup)
}
feedback <- c(feedback, "", fbtmp)
}
}
## rubrics (information for teacher), add to feedback collection
if(length(rblock)){
rblock <- paste0("<p>", stripdiv(xml2::xml_contents(rblock)), "</p>", collapse = "")
if(!rawHTML){
rblock <- equation(rblock)
rblock <- pandoc(rblock,
from = "html+tex_math_dollars+tex_math_single_backslash",
to = markup)
}
feedback <- c(feedback, "", rblock)
}
## further meta-information tags
exother <- if(extype == "num" && !is.null(extol)) {
sprintf(if(markup == "markdown_strict") "extol: %s\n" else "\\extol{%s}\n", extol)
} else if(grepl("choice", extype)) {
sprintf(if(markup == "markdown_strict") "exshuffle: %s\n" else "\\exshuffle{%s}\n", exshuffle)
} else {
""
}
if(shareStats) {
splitinfo <- unlist(strsplit(taxstr, ";"))
splitinfo[splitinfo == "na"] <- NA
splitinfo <- sprintf(if(markup == "markdown_strict")
"exsection: %s\nexextra[Type]: %s\nexextra[Program]: %s\nexextra[Language]: %s\nexextra[Level]: %s" else
"\\exsection{%s}\n\\exextra[Type]{%s}\n\\exextra[Program]{%s}\n\\exextra[Language]{%s}\n\\exextra[Level]{%s}",
splitinfo[1], splitinfo[2], splitinfo[3], splitinfo[4], splitinfo[5])
exother <- paste0(exother, splitinfo, collapse = "\n")
}
if(extype %in% c("schoice", "mchoice") & (length(solutions) < 1)) {
solutions <- as.list(ifelse(sol, "True", "False"))
}
## insert information into template
exrc <- sprintf(tmpl,
paste(qtext, collapse = "\n"),
if(extype %in% c("schoice", "mchoice"))
paste(c("", answerlist_env(answers)), collapse = "\n") else "",
if(length(solutions) >= 1L || !is.null(feedback))
paste(solution_env(solutions, feedback), collapse = "\n") else "",
exname,
extype,
exsol,
exother)
exrc <- gsub("#38;", "", exrc)
exrc <- gsub("&", "&", exrc)
exrc <- gsub("\\\\href", "\\\\url", exrc)
exrc <- gsub("\\\\textbackslash\\s*", "\\\\", exrc)
exrc <- exfile(exrc)
## supplements.
if(length(supps)) {
scode <- if(markup == "markdown_strict") {
'```{r, echo = FALSE, results = "hide"}'
} else {
'<<echo=FALSE, results=hide>>='
}
for(f in supps)
scode <- c(scode, paste0('include_supplement("', f, '")'))
scode <- c(scode, if(markup == "markdown_strict") {
'```'
} else {
'@'
})
exrc <- c(scode, "", exrc)
}
## default file name
exname <- name_to_file(exname)
}
## write/return resulting exercises
names(exrc) <- exname
if(!is.null(dir)) {
dir.create(tdir <- tempfile())
writeLines(exrc, file.path(tdir, paste(exname, fext, sep = ".")))
fn <- dir(tdir)
file.copy(file.path(tdir, fn), file.path(dir, fn), overwrite = TRUE)
unlink(tdir)
invisible(exrc)
} else {
return(exrc)
}
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.