Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-language chunks and chained setup chunks #390

Merged
merged 20 commits into from
Jul 14, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bf9e665
initial working version of chained setup chunks
nischalshrestha Jun 16, 2020
e6a73b6
fixed a setup chain bug and added error handling for exercise.setup l…
nischalshrestha Jun 17, 2020
5f24a96
Merge branch 'master' into feature/chained-setup-chunks
nischalshrestha Jun 17, 2020
5086bd5
better error handling of cycles, and added tests for cycles
nischalshrestha Jun 17, 2020
7497412
addressed some suggestions and handled default exercise.setup case
nischalshrestha Jun 18, 2020
03eb521
move knitr setting to rmd, dont process empty chunks
nischalshrestha Jun 20, 2020
cc57a2f
addressing suggestions and fixing issue with losing chunk structure
nischalshrestha Jun 23, 2020
cca5524
Update NEWS.md
nischalshrestha Jun 23, 2020
6c9a111
cleaning up code
nischalshrestha Jun 23, 2020
2e229ae
Updates:
nischalshrestha Jun 30, 2020
22d2a60
More updates:
nischalshrestha Jul 2, 2020
085c255
Updates:
nischalshrestha Jul 6, 2020
5aef409
Update R/initialize.R
nischalshrestha Jul 7, 2020
76c0226
Updates:
nischalshrestha Jul 7, 2020
447e3a0
Merge branch 'feature/chained-setup-chunks' of github.com:rstudio/lea…
nischalshrestha Jul 7, 2020
e7c56ca
Updates:
nischalshrestha Jul 9, 2020
fad2cd6
remove checker_fn_exists variable and use explicit check instead
nischalshrestha Jul 13, 2020
d3eccbd
change envir_result to envir in exercise.R
nischalshrestha Jul 13, 2020
747d081
variable assignment cleanup for output_file
nischalshrestha Jul 13, 2020
1e67b85
Merge branch 'master' into feature/chained-setup-chunks
nischalshrestha Jul 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 33 additions & 25 deletions R/exercise.R
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,17 @@ setup_exercise_handler <- function(exercise_rx, session) {
exercise$global_setup <- get_global_setup()
# retrieve exercise cache information:
# - chunks (setup + exercise) for the exercise to be processed in `evaluate_exercise`
# - checker code
# - checker code (check, code-check)
# - solution
# - engine
exercise <- append(exercise, get_exercise_cache(exercise$label))
if (!isTRUE(exercise$should_check)) {
exercise$check <- NULL
exercise$code_check <- NULL
}
# variable has now served its purpose so remove it
exercise$should_check <- NULL

# placeholder for current learnr version to deal with exercise structure differences
# with other learnr versions
exercise$version <- "1"
Expand Down Expand Up @@ -111,7 +119,7 @@ setup_exercise_handler <- function(exercise_rx, session) {
timeout_exceeded = result$timeout_exceeded,
time_elapsed = as.numeric(difftime(Sys.time(), start, units="secs")),
error_message = result$error_message,
checked = !is.null(exercise$code_check) || !is.null(exercise$checker),
checked = !is.null(exercise$code_check) || !is.null(exercise$check),
feedback = result$feedback
)

Expand All @@ -136,7 +144,7 @@ setup_exercise_handler <- function(exercise_rx, session) {
}

# helper function that will upgrade a previous learnr exercise into new learnr exercise
# TODO-do the actual upgrade
# TODO: do the actual upgrade
upgrade_exercise <- function(exercise) {
# if version doesn't exist we're at "0" (older learnr)
if (is.null(exercise$version)) {
Expand Down Expand Up @@ -458,30 +466,30 @@ evaluate_exercise <- function(exercise, envir, evaluate_global_setup = FALSE) {
dependencies
)

checker_fn_does_not_exist <- is.null(exercise$check) || is.null(checker)
if (checker_fn_does_not_exist)
checker <- function(...) { NULL }

# call the checker
tryCatch({
checker_feedback <- checker(
label = exercise$label,
user_code = exercise$code,
solution_code = exercise$solution,
check_code = exercise$checker, # use the cached checker for exercise
envir_result = envir,
evaluate_result = evaluate_result,
envir_prep = envir_prep,
last_value = last_value
)
}, error = function(e) {
err <<- e$message
message("Error occured while evaluating 'exercise.checker'. Error:\n", e)
})
if (!is.null(err)) {
return(error_result("Error occured while evaluating 'exercise.checker'."))
checker_feedback <- NULL
if (!is.null(exercise$check) && exercise_checker_exists) {
# call the checker
tryCatch({
checker_feedback <- checker(
label = exercise$label,
user_code = exercise$code,
solution_code = exercise$solution,
check_code = exercise$check, # use the cached checker for exercise
envir_result = envir,
evaluate_result = evaluate_result,
envir_prep = envir_prep,
last_value = last_value
)
}, error = function(e) {
err <<- e$message
message("Error occured while evaluating 'exercise.checker'. Error:\n", e)
})
if (!is.null(err)) {
return(error_result("Error occured while evaluating 'exercise.checker'."))
}
}


# validate the feedback
feedback_validated(checker_feedback)

Expand Down
26 changes: 18 additions & 8 deletions R/knitr-hooks.R
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,6 @@ install_knitr_hooks <- function() {
parent_setup_chunks
}

# TODO-Nischal restructure of each chunk into an acceptable structure for srvr
# example: {"code":["x <- 2"],"opts":{"chunk_opts":{"label":["setupB"]}}}
# helper function to return a list of exercise chunk and its setup chunks
get_all_chunks <- function(options) {
# get the exercise chunk
Expand Down Expand Up @@ -160,6 +158,8 @@ install_knitr_hooks <- function() {
options$eval <- options$exercise.eval
else
options$eval <- FALSE
# exercises can be support chunks, but if it's an exercise it should be treated that way
return(options)
}

# if this is an exercise support chunk then force echo, but don't
Expand All @@ -171,7 +171,7 @@ install_knitr_hooks <- function() {
options$highlight <- FALSE
}

if (is_exercise_support_chunk(options, type = "check")) {
if (is_exercise_support_chunk(options, type = c("code-check", "check"))) {
options$include <- FALSE
schloerke marked this conversation as resolved.
Show resolved Hide resolved
}

Expand All @@ -198,6 +198,7 @@ install_knitr_hooks <- function() {

# set the eval property as appropriate
options$eval <- exercise_eval
options$echo <- FALSE
}

# return modified options
Expand Down Expand Up @@ -261,13 +262,18 @@ install_knitr_hooks <- function() {
options$engine <- knitr_engine(options$engine)
all_chunks <- get_all_chunks(options)

code_check_chunk <- get_knitr_chunk(paste0(options$label, "-code-check"))
check_chunk <- get_knitr_chunk(paste0(options$label, "-check"))
ui_options$check = !is.null(check_chunk)
solution <- get_knitr_chunk(paste0(options$label, "-solution"))
ui_options$has_checker = !is.null(check_chunk) || !is.null(code_check_chunk)

exercise_cache <- list(chunks = all_chunks,
checker = check_chunk,
code_check = code_check_chunk,
check = check_chunk,
solution = solution,
options = options,
engine = options$engine)

# serialize the list of chunks to server
rmarkdown::shiny_prerendered_chunk(
'server',
Expand All @@ -288,11 +294,15 @@ install_knitr_hooks <- function() {
exercise_wrapper_div(extra_html = extra_html)
}

# handle exercise support chunks (setup, hints, solution)
# handle exercise support chunks (hints, solution)
else if (is_exercise_support_chunk(options)) {

# checking code (-check) is included in exercise cache
if (is_exercise_support_chunk(options, type = c("setup", "hint", "hint-\\d+", "solution"))) {
# setup and checking code (-setup, -code-check, and -check) are included in exercise cache
# do not send the setup and checking code to the browser

# send hint and solution to the browser
# these are visibly displayed in the UI
if (is_exercise_support_chunk(options, type = c("hint", "hint-\\d+", "solution"))) {
exercise_wrapper_div(suffix = "support")
}

Expand Down
26 changes: 6 additions & 20 deletions inst/lib/tutorial/tutorial.js
Original file line number Diff line number Diff line change
Expand Up @@ -880,13 +880,15 @@ Tutorial.prototype.$initializeExerciseEditors = function() {
else
code = code + $(this).text();
});
console.error("code ", code, code_blocks)
nischalshrestha marked this conversation as resolved.
Show resolved Hide resolved
code_blocks.remove();
// ensure a minimum of 3 lines
var lines = code.split(/\r\n|\r|\n/).length;
for (var i=lines; i<thiz.kMinLines;i++)
code = code + "\n";



// get the knitr options script block and detach it (will move to input div)
var options_script = exercise.children('script[data-ui-opts="1"]').detach();

Expand Down Expand Up @@ -932,7 +934,7 @@ Tutorial.prototype.$initializeExerciseEditors = function() {

var chunk_options = options_script.length == 1 ? JSON.parse(options_script.text()) : {};
// create submit answer button if checks are enabled
if (chunk_options["check"])
if (chunk_options.has_checker)
add_submit_button("fa-check-square-o", "btn-primary", "Submit Answer", true);

// create run button
Expand Down Expand Up @@ -1320,23 +1322,7 @@ Tutorial.prototype.$initializeExerciseEvaluation = function() {
// restore flag
value.restore = this.restore;

// get any setup, solution, or check chunks

// setup
var label = exerciseLabel(el);
value.setup = null;
// solution
value.solution = thiz.$exerciseSupportCode(label + "-solution");

value.check = null;
// check
if (this.check) {
value.code_check = thiz.$exerciseSupportCode(label + "-code-check");
// check if exercise needs to be graded
var options_script = thiz.$exerciseContainer(el).find('script[data-ui-opts="1"]');
var chunk_options = options_script.length == 1 ? JSON.parse(options_script.text()) : {};
value.check = chunk_options["check"];
}
value.should_check = this.should_check;

// some randomness to ensure we re-execute on button clicks
value.timestamp = new Date().getTime();
Expand All @@ -1350,13 +1336,13 @@ Tutorial.prototype.$initializeExerciseEvaluation = function() {
this.runButtons(el).on('click.exerciseInputBinding', function(ev) {
binding.restore = false;
binding.clicked = true;
binding.check = ev.target.hasAttribute('data-check');
binding.should_check = ev.target.hasAttribute('data-check');
callback(true);
});
$(el).on('restore.exerciseInputBinding', function(ev, options) {
binding.restore = true;
binding.clicked = false;
binding.check = options.check;
binding.should_check = options.check;
nischalshrestha marked this conversation as resolved.
Show resolved Hide resolved
callback(true);
});
},
Expand Down