├── .Rbuildignore ├── assets └── hex-riddlr.kra ├── inst ├── www │ ├── hex-riddlr.png │ └── riddlr.css └── example │ ├── shiny │ ├── question_catalog │ │ ├── install.R │ │ ├── welcome.md │ │ └── app.R │ └── single_question │ │ └── app.R │ └── questions │ ├── q2.riddlr.Rmd │ ├── q5.riddlr.Rmd │ ├── q1.riddlr.Rmd │ ├── q4.riddlr.Rmd │ └── q3.riddlr.Rmd ├── man ├── parse_safe.Rd ├── success_response.Rd ├── riddle_response_html.Rd ├── parse_error_response.Rd ├── eval_with_timeout.Rd ├── timeout_response.Rd ├── error_response.Rd ├── update_test_progress_html.Rd ├── with_env_cleanup.Rd ├── timeout_error_cpu.Rd ├── incorrect_solution_response.Rd ├── riddlr_base_env.Rd ├── is_timeout_error.Rd ├── grace_timeouts_response.Rd ├── opts.Rd └── grade_riddle.Rd ├── R ├── shiny_riddlr_resource.R ├── shiny_actionButtonPrimary.R ├── riddlr_env.R ├── opts.R ├── riddlr_result.R ├── utils.R ├── run_riddle.R ├── shiny_riddlr_module.R ├── parse_riddlr_rmd.R └── grade_riddle.R ├── riddlr.Rproj ├── DESCRIPTION ├── .gitignore ├── LICENSE ├── NAMESPACE └── README.md /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^assets 4 | -------------------------------------------------------------------------------- /assets/hex-riddlr.kra: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgkf/riddlr/HEAD/assets/hex-riddlr.kra -------------------------------------------------------------------------------- /inst/www/hex-riddlr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dgkf/riddlr/HEAD/inst/www/hex-riddlr.png -------------------------------------------------------------------------------- /man/parse_safe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grade_riddle.R 3 | \name{parse_safe} 4 | \alias{parse_safe} 5 | \title{Parsing with error handling} 6 | \usage{ 7 | parse_safe(...) 8 | } 9 | \description{ 10 | Parsing with error handling 11 | } 12 | -------------------------------------------------------------------------------- /man/success_response.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grade_riddle.R 3 | \name{success_response} 4 | \alias{success_response} 5 | \title{Response for successful solution} 6 | \usage{ 7 | success_response(hash = runif(1)) 8 | } 9 | \description{ 10 | Response for successful solution 11 | } 12 | -------------------------------------------------------------------------------- /man/riddle_response_html.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grade_riddle.R 3 | \name{riddle_response_html} 4 | \alias{riddle_response_html} 5 | \title{Format a riddlr response as HTML output} 6 | \usage{ 7 | riddle_response_html(x) 8 | } 9 | \description{ 10 | Format a riddlr response as HTML output 11 | } 12 | -------------------------------------------------------------------------------- /man/parse_error_response.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grade_riddle.R 3 | \name{parse_error_response} 4 | \alias{parse_error_response} 5 | \title{Response for a solutions that produce errors} 6 | \usage{ 7 | parse_error_response(x, ...) 8 | } 9 | \description{ 10 | Response for a solutions that produce errors 11 | } 12 | -------------------------------------------------------------------------------- /man/eval_with_timeout.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grade_riddle.R 3 | \name{eval_with_timeout} 4 | \alias{eval_with_timeout} 5 | \title{Evaluate and memoise solution execution} 6 | \usage{ 7 | eval_with_timeout(x, timeout = Inf, envir = parent.frame()) 8 | } 9 | \description{ 10 | Evaluate and memoise solution execution 11 | } 12 | -------------------------------------------------------------------------------- /man/timeout_response.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grade_riddle.R 3 | \name{timeout_response} 4 | \alias{timeout_response} 5 | \title{Response for a solution that times out} 6 | \usage{ 7 | timeout_response(x, solution_output, input_env, test_i, test_n) 8 | } 9 | \description{ 10 | Response for a solution that times out 11 | } 12 | -------------------------------------------------------------------------------- /man/error_response.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grade_riddle.R 3 | \name{error_response} 4 | \alias{error_response} 5 | \title{Response for a solutions that produce errors} 6 | \usage{ 7 | error_response(x, solution_output, input_env, test_i, test_n) 8 | } 9 | \description{ 10 | Response for a solutions that produce errors 11 | } 12 | -------------------------------------------------------------------------------- /R/shiny_riddlr_resource.R: -------------------------------------------------------------------------------- 1 | #' 2 | #' @importFrom shiny addResourcePath singleton tags 3 | #' @export 4 | #' 5 | riddlr_css <- function() { 6 | shiny::addResourcePath("riddlr", system.file("www", package = "riddlr")) 7 | shiny::singleton(shiny::tags$head(shiny::tags$link( 8 | id = "riddlr-css", 9 | rel = "stylesheet", 10 | type = "text/css", 11 | href = "riddlr/riddlr.css" 12 | ))) 13 | } 14 | -------------------------------------------------------------------------------- /man/update_test_progress_html.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grade_riddle.R 3 | \name{update_test_progress_html} 4 | \alias{update_test_progress_html} 5 | \title{Format test results as html for updating progress text} 6 | \usage{ 7 | update_test_progress_html(test_results, reactive_output) 8 | } 9 | \description{ 10 | Format test results as html for updating progress text 11 | } 12 | -------------------------------------------------------------------------------- /man/with_env_cleanup.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/riddlr_env.R 3 | \name{with_env_cleanup} 4 | \alias{with_env_cleanup} 5 | \title{Helper to ensure that any library calls get undone by the end of evaluation} 6 | \usage{ 7 | with_env_cleanup(expr, envir = parent.frame()) 8 | } 9 | \description{ 10 | Helper to ensure that any library calls get undone by the end of evaluation 11 | } 12 | -------------------------------------------------------------------------------- /R/shiny_actionButtonPrimary.R: -------------------------------------------------------------------------------- 1 | modifyCssClasses <- function(x, ...) { 2 | dots <- as.list(match.call())[-1] 3 | dots <- gsub("\\s+", "", as.character(dots[names(dots) == ""])) 4 | 5 | classes <- strsplit(x$attribs$class, " ")[[1]] 6 | classes <- setdiff(classes, gsub("^-", "", dots[grepl("^-", dots)])) 7 | classes <- sort(c(classes, dots[grepl("^[^-]", dots)])) 8 | 9 | x$attribs$class <- paste(classes, collapse = " ") 10 | x 11 | } 12 | -------------------------------------------------------------------------------- /man/timeout_error_cpu.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \docType{data} 4 | \name{timeout_error_cpu} 5 | \alias{timeout_error_cpu} 6 | \title{compute a timeout error from setTimeLimit} 7 | \format{ 8 | An object of class \code{NULL} of length 0. 9 | } 10 | \usage{ 11 | timeout_error_cpu 12 | } 13 | \description{ 14 | compute a timeout error from setTimeLimit 15 | } 16 | \keyword{datasets} 17 | -------------------------------------------------------------------------------- /man/incorrect_solution_response.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grade_riddle.R 3 | \name{incorrect_solution_response} 4 | \alias{incorrect_solution_response} 5 | \title{Response for an incorrect solution} 6 | \usage{ 7 | incorrect_solution_response( 8 | user_output, 9 | solution_output, 10 | input_env, 11 | test_i, 12 | test_n, 13 | err 14 | ) 15 | } 16 | \description{ 17 | Response for an incorrect solution 18 | } 19 | -------------------------------------------------------------------------------- /man/riddlr_base_env.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/riddlr_env.R 3 | \docType{data} 4 | \name{riddlr_base_env} 5 | \alias{riddlr_base_env} 6 | \title{Build a base environment for solutions to be evaluated within} 7 | \format{ 8 | An object of class \code{environment} of length 0. 9 | } 10 | \usage{ 11 | riddlr_base_env 12 | } 13 | \description{ 14 | Build a base environment for solutions to be evaluated within 15 | } 16 | \keyword{datasets} 17 | -------------------------------------------------------------------------------- /riddlr.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /man/is_timeout_error.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{is_timeout_error} 4 | \alias{is_timeout_error} 5 | \title{Compare an error against a timeout error from setTimeLimit} 6 | \usage{ 7 | is_timeout_error(e) 8 | } 9 | \arguments{ 10 | \item{e}{an error to compare} 11 | } 12 | \value{ 13 | a logical indicating whether e is a setTimeLimit timeout error 14 | } 15 | \description{ 16 | Compare an error against a timeout error from setTimeLimit 17 | } 18 | -------------------------------------------------------------------------------- /man/grace_timeouts_response.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grade_riddle.R 3 | \name{grace_timeouts_response} 4 | \alias{grace_timeouts_response} 5 | \title{Response for a solution that times out only a grace period} 6 | \usage{ 7 | grace_timeouts_response( 8 | user_output, 9 | solution_output, 10 | input_env, 11 | test_i, 12 | test_n, 13 | time_frac 14 | ) 15 | } 16 | \description{ 17 | Response for a solution that times out only a grace period 18 | } 19 | -------------------------------------------------------------------------------- /inst/www/riddlr.css: -------------------------------------------------------------------------------- 1 | .riddlr-run-btns { 2 | float: right; 3 | } 4 | 5 | /* 6 | #riddlr_submit { 7 | background: rgb(24,188,156); 8 | border: 0 solid; 9 | } 10 | */ 11 | 12 | .alert { 13 | border-radius: 0; 14 | } 15 | 16 | .riddlr-alert code { 17 | background: none; 18 | } 19 | 20 | .riddlr-alert pre { 21 | /* color: white; */ 22 | border: 0 solid; 23 | background-color: #555; 24 | background-color: rgba(255, 255, 255, 0.1); 25 | } 26 | 27 | pre { 28 | border: #222; 29 | border: 1px solid rgba(0, 0, 0, 0.1); 30 | border-radius: 0; 31 | } 32 | -------------------------------------------------------------------------------- /inst/example/shiny/question_catalog/install.R: -------------------------------------------------------------------------------- 1 | if_unavailable <- function(pkg, expr, envir = parent.frame()) 2 | if (!pkg %in% installed.packages()) invisible(eval(expr, envir = envir)) 3 | 4 | if_unavailable("devtools", install.packages("devtools")) 5 | if_unavailable("riddlr", devtools::install_github("dgkf/riddlr")) 6 | if_unavailable("shinythemes", install.packages("shinythemes")) 7 | if_unavailable("shinydashboard", install.packages("shinydashboard")) 8 | if_unavailable("shinycssloaders", install.packages("shinycssloaders")) 9 | if_unavailable("dplyr", install.packages("dplyr")) 10 | if_unavailable("tidyr", install.packages("tidyr")) 11 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Type: Package 2 | Package: riddlr 3 | Title: A package for building interactive programming challenges 4 | Version: 0.0.1 5 | Authors@R: 6 | person(given = "Doug", 7 | family = "Kelkhoff", 8 | role = c("aut", "cre"), 9 | email = "doug.kelkhoff@gmail.com") 10 | Description: A package for building interactive programming 11 | challenges 12 | License: MIT 13 | BugReports: https://github.com/dgkf/riddlr 14 | Imports: 15 | utils, 16 | shiny, 17 | shinyAce (>= 0.4), 18 | rmarkdown, 19 | markdown, 20 | memoise, 21 | yaml 22 | VignetteBuilder: 23 | knitr 24 | Encoding: UTF-8 25 | LazyData: true 26 | RoxygenNote: 7.1.1 27 | -------------------------------------------------------------------------------- /R/riddlr_env.R: -------------------------------------------------------------------------------- 1 | #' Build a base environment for solutions to be evaluated within 2 | riddlr_base_env <- Reduce(function(l, r) { parent.env(l) <- r; l }, 3 | lapply(c("methods", "datasets", "utils", "tools", "stats"), getNamespace), 4 | init = new.env(parent = getNamespace("base"))) 5 | 6 | 7 | 8 | #' Helper to ensure that any library calls get undone by the end of evaluation 9 | with_env_cleanup <- function(expr, envir = parent.frame()) { 10 | start_env_pos <- length(search()) 11 | on.exit({ 12 | while (length(search()) > start_env_pos) 13 | detach(attr(pos.to.env(2L), "name"), 14 | unload = TRUE, 15 | character.only = TRUE) 16 | }) 17 | eval(expr, envir = envir) 18 | } 19 | -------------------------------------------------------------------------------- /inst/example/shiny/single_question/app.R: -------------------------------------------------------------------------------- 1 | library(riddlr) 2 | library(shiny) 3 | library(shinyAce) 4 | library(markdown) 5 | 6 | # ensure interactive console width won't affect output 7 | options(width = 80) 8 | 9 | r <- parse_riddlr_rmd(system.file( 10 | "example", 11 | "questions", 12 | "q3.riddlr.Rmd", 13 | package = "riddlr")) 14 | 15 | ui <- fluidPage( 16 | riddle_ui("riddle", 17 | question_ui = r$prompt, 18 | metadata = r$metadata)) 19 | 20 | server <- function(input, output, session) { 21 | observe(callModule(riddle, "riddle", 22 | solution = r$grader$solution, 23 | quoted = TRUE, 24 | test_inputs = r$grader$test_inputs, 25 | test_timeouts = r$grader$test_timeouts)) 26 | } 27 | 28 | shinyApp(ui, server) 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # History files 2 | .Rhistory 3 | .Rapp.history 4 | 5 | # Session Data files 6 | .RData 7 | 8 | # User-specific files 9 | .Ruserdata 10 | 11 | # Example code in package build process 12 | *-Ex.R 13 | 14 | # Output files from R CMD build 15 | /*.tar.gz 16 | 17 | # Output files from R CMD check 18 | /*.Rcheck/ 19 | 20 | # RStudio files 21 | .Rproj.user/ 22 | 23 | # produced vignettes 24 | vignettes/*.html 25 | vignettes/*.pdf 26 | 27 | # OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 28 | .httr-oauth 29 | 30 | # knitr and R markdown default cache directories 31 | *_cache/ 32 | /cache/ 33 | 34 | # Temporary files created by R markdown 35 | *.utf8.md 36 | *.knit.md 37 | 38 | # R Environment Variables 39 | .Renviron 40 | -------------------------------------------------------------------------------- /man/opts.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/opts.R 3 | \docType{data} 4 | \name{opts} 5 | \alias{opts} 6 | \title{Getters and setters for riddlr options} 7 | \format{ 8 | An object of class \code{list} of length 2. 9 | } 10 | \usage{ 11 | opts 12 | } 13 | \description{ 14 | \describe{ 15 | \item{onSubmit}{ 16 | Expects a function expecting arguments \code{code}, \code{duration} and 17 | \code{response}. This function is called after a question solution is 18 | submitted and graded and can be used as a callback for additional 19 | functionality or logging. 20 | } 21 | } 22 | } 23 | \examples{ 24 | opts$set(onSubmit = function(code, duration, response) message("submitted")) 25 | 26 | } 27 | \keyword{datasets} 28 | -------------------------------------------------------------------------------- /R/opts.R: -------------------------------------------------------------------------------- 1 | #' Getters and setters for riddlr options 2 | #' 3 | #' \describe{ 4 | #' \item{onSubmit}{ 5 | #' Expects a function expecting arguments \code{code}, \code{duration} and 6 | #' \code{response}. This function is called after a question solution is 7 | #' submitted and graded and can be used as a callback for additional 8 | #' functionality or logging. 9 | #' } 10 | #' } 11 | #' 12 | #' @importFrom utils modifyList 13 | #' @export 14 | #' 15 | #' @examples 16 | #' opts$set(onSubmit = function(code, duration, response) message("submitted")) 17 | #' 18 | opts <- list( 19 | get = function(name) riddlr:::.opts[[name]], 20 | set = function(...) { 21 | assignInMyNamespace(".opts", utils::modifyList(riddlr:::.opts, list(...))) 22 | }) 23 | 24 | 25 | 26 | .opts <- list() 27 | .opts$onSubmit <- function(code, duration, response) { } 28 | -------------------------------------------------------------------------------- /man/grade_riddle.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grade_riddle.R 3 | \name{grade_riddle} 4 | \alias{grade_riddle} 5 | \title{function for generating an exercise checker for a puzzlr challenge} 6 | \usage{ 7 | grade_riddle( 8 | user_code, 9 | solution, 10 | test_inputs, 11 | test_timeouts = Inf, 12 | grace_timeouts = test_timeouts * 5, 13 | test_details = rep(list(NULL), length(test_inputs)), 14 | quoted = FALSE 15 | ) 16 | } 17 | \arguments{ 18 | \item{solution}{solution code} 19 | 20 | \item{envs}{environments in which solution should be evaluated. these should 21 | contain the necessary input variables for the challenge.} 22 | } 23 | \value{ 24 | A shiny ui element displaying output from grading the question 25 | solution. 26 | } 27 | \description{ 28 | function for generating an exercise checker for a puzzlr challenge 29 | } 30 | -------------------------------------------------------------------------------- /R/riddlr_result.R: -------------------------------------------------------------------------------- 1 | riddlr_result <- function(result, expr = NULL, duration = NULL, 2 | console = NULL, warnings = NULL) { 3 | 4 | r_class <- c("riddlr_result", class(result)) 5 | if (!is.null(warnings)) r_class <- c(r_class, "warning") 6 | 7 | structure(result, 8 | expr = expr, 9 | warnings = warnings, 10 | duration = duration, 11 | console = console, 12 | class = r_class) 13 | } 14 | 15 | 16 | 17 | unclass_riddlr_result <- function(x) { 18 | x <- unclass(x) 19 | attributes(x) <- NULL 20 | x 21 | } 22 | 23 | 24 | 25 | #' @export 26 | print.riddlr_result <- function(x, ...) { 27 | print(unclass_riddlr_result(x), ...) 28 | } 29 | 30 | 31 | 32 | format.riddlr_result <- function(x, ...) { 33 | format(unclass_riddlr_result(x), ...) 34 | } 35 | 36 | 37 | 38 | Ops.riddlr_result <- function(e1, e2) { 39 | if (.Generic == "==") { 40 | e1 <- unclass_riddlr_result(e1) 41 | e2 <- unclass_riddlr_result(e2) 42 | return(identical(e1, e2)) 43 | } else if (.Generic == "!=") { 44 | return(!(e1 == e2)) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 dgkf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(print,riddlr_result) 4 | export(bootstrapify) 5 | export(error_response) 6 | export(grace_timeouts_response) 7 | export(grade_riddle) 8 | export(is_timeout_error) 9 | export(modifyCssClasses) 10 | export(opts) 11 | export(parse_error_response) 12 | export(parse_riddlr_dir_headers) 13 | export(parse_riddlr_rmd) 14 | export(riddle) 15 | export(riddle_response_html) 16 | export(riddle_ui) 17 | export(riddlr_css) 18 | export(run_riddle) 19 | export(success_response) 20 | export(timeout_response) 21 | importFrom(markdown,markdownToHTML) 22 | importFrom(memoise,memoise) 23 | importFrom(rmarkdown,html_fragment) 24 | importFrom(rmarkdown,render) 25 | importFrom(rmarkdown,yaml_front_matter) 26 | importFrom(shiny,HTML) 27 | importFrom(shiny,addResourcePath) 28 | importFrom(shiny,div) 29 | importFrom(shiny,h2) 30 | importFrom(shiny,is.reactive) 31 | importFrom(shiny,observeEvent) 32 | importFrom(shiny,reactiveVal) 33 | importFrom(shiny,renderUI) 34 | importFrom(shiny,setProgress) 35 | importFrom(shiny,singleton) 36 | importFrom(shiny,tagList) 37 | importFrom(shiny,tags) 38 | importFrom(shiny,withProgress) 39 | importFrom(shinyAce,aceAnnotate) 40 | importFrom(shinyAce,aceAutocomplete) 41 | importFrom(shinyAce,aceEditor) 42 | importFrom(shinyAce,aceTooltip) 43 | importFrom(utils,modifyList) 44 | -------------------------------------------------------------------------------- /inst/example/questions/q2.riddlr.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Dealing with Filenames" 3 | # author: "Your Name" # Optionally take some credit for submitting a question! 4 | difficulty: 0.1 5 | tags: 6 | - filesystem 7 | - tools 8 | details: > 9 | Handle filepaths to get the name of a file without the trailing extension 10 | --- 11 | 12 | 13 | Given a character vector of filepaths (such as `/home/id/project/analysis.R`), 14 | return the name of the filename without the file extension. 15 | 16 | For example, if given the vector 17 | 18 | ```{} 19 | filepaths <- c("/home/id/project/analysis.R", "/home/id/project/report.Rmd") 20 | ``` 21 | 22 | The answer would be 23 | 24 | ```{} 25 | c("analysis", "report") 26 | ``` 27 | 28 | 29 | 30 | ```{r grader} 31 | library(tools) 32 | 33 | test_inputs <- list( 34 | list(filepaths = c( 35 | "/home/id/project/analysis.R", 36 | "/home/id/project/report.Rmd")), 37 | list(filepaths = c( 38 | "/home/id/project/analysis.R", 39 | "/home/id/project/analysis.knit.Rmd", 40 | "/home/id/project/.Rprofile"))) 41 | 42 | list( 43 | test_inputs = test_inputs, 44 | test_timeouts = 0.1, 45 | solution = quote(tools::file_path_sans_ext(basename(filepaths))), 46 | quoted = TRUE) 47 | ``` 48 | 49 | ```{r starter, eval = FALSE} 50 | # already in environment: 51 | # tools package is available to use 52 | # filepaths (a character vector of filepaths, 1 <= length <= 10) 53 | 54 | library(tools) 55 | 56 | print(filepaths) 57 | ``` 58 | -------------------------------------------------------------------------------- /inst/example/questions/q5.riddlr.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Finding String Matches" 3 | # author: "Your Name" # Optionally take some credit for submitting a question! 4 | difficulty: 0.2 5 | tags: 6 | - strings 7 | details: > 8 | Find matches of an entire word within a vector of strings 9 | --- 10 | 11 | 12 | 13 | You're given a vector of strings and a keyword. Return a vector containing only the strings that contained the keyword as a whole word (not as part of a word). 14 | 15 | For example, with the following input 16 | 17 | ```{} 18 | strings <- c("podcast","castle","cast off","plaster cast") 19 | keyword <- "cast" 20 | ``` 21 | 22 | The answer would be 23 | 24 | ```{} 25 | c("cast off", "plaster cast") 26 | ``` 27 | 28 | 29 | 30 | ```{r grader} 31 | # 32 | # a code chunk named 'grader' should produce a list of arguments to use with the 33 | # grade_riddle function. 34 | # 35 | 36 | list( 37 | test_inputs = list( 38 | list( 39 | strings = c("code red","decode","coding is fun"), 40 | keyword = "code" 41 | ), 42 | list( 43 | strings = c("sawyer","chain saw","I saw you yesterday."), 44 | keyword = "saw" 45 | ), 46 | list( 47 | strings = c("robotics","build a robot","robots are taking over"), 48 | keyword = "robot" 49 | ) 50 | ), 51 | test_timeouts = 0.1, 52 | solution = quote({ 53 | strings[grepl(paste0("\\b",keyword,"\\b"),strings, ignore.case = TRUE)] 54 | }), 55 | quoted = TRUE) 56 | ``` 57 | 58 | ```{r starter, eval = FALSE} 59 | # already in environment: 60 | # strings (a character vector of strings, 1 < length <= 10) 61 | # keyword (a string) 62 | 63 | print(strings) 64 | print(keyword) 65 | ``` 66 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | `%||%` <- function(lhs, rhs) if (is.null(lhs) || !length(lhs)) rhs else lhs 2 | 3 | 4 | 5 | #' Compare an error against a timeout error from setTimeLimit 6 | #' 7 | #' @param e an error to compare 8 | #' 9 | #' @return a logical indicating whether e is a setTimeLimit timeout error 10 | #' @export 11 | is_timeout_error <- function(e) { 12 | if (!"error" %in% class(e)) return(FALSE) 13 | (identical(attributes(e), attributes(timeout_error_cpu)) || 14 | identical(attributes(e), attributes(timeout_error_elapsed))) && 15 | (identical(e$message, timeout_error_cpu$message) || 16 | identical(e$message, timeout_error_elapsed$message)) 17 | } 18 | 19 | #' compute a timeout error from setTimeLimit 20 | timeout_error_cpu <- tryCatch({ 21 | setTimeLimit(cpu = 0.0001, elapsed = Inf, transient = TRUE) 22 | Sys.sleep(0.001) 23 | }, error = function(e) e) 24 | 25 | timeout_error_elapsed <- tryCatch({ 26 | setTimeLimit(cpu = Inf, elapsed = 0.0001, transient = TRUE) 27 | Sys.sleep(0.001) 28 | }, error = function(e) e) 29 | 30 | 31 | 32 | is_error <- function(x) return(inherits(x, "error")) 33 | is_warning <- function(x) return(inherits(x, "warning")) 34 | 35 | 36 | 37 | #' @export 38 | modifyCssClasses <- function(x, ...) { 39 | dots <- as.list(match.call())[-1] 40 | dots <- gsub("\\s+", "", as.character(dots[names(dots) == ""])) 41 | 42 | classes <- strsplit(x$attribs$class, " ")[[1]] 43 | classes <- setdiff(classes, gsub("^-", "", dots[grepl("^-", dots)])) 44 | classes <- sort(c(classes, dots[grepl("^[^-]", dots)])) 45 | 46 | x$attribs$class <- paste(classes, collapse = " ") 47 | x 48 | } 49 | 50 | 51 | 52 | #' @export 53 | bootstrapify <- function(x) { 54 | gsub("