Skip to content

Commit

Permalink
Hash Generation feature added (OpenIntroStat#159)
Browse files Browse the repository at this point in the history
Before the "Congratulations" page, there is now a "Submit" page that generates a hash that can be submitted.

As per the discussion in OpenIntroStat#159, I modified the encoder_ui to require students to enter a name and ID. This name and ID are then encoded in the hash (as an "identifier" type), so that submitted names and IDs could theoretically be used to track tutorial completion.
  • Loading branch information
mamcisaac committed Jul 19, 2023
1 parent 75d9415 commit 08a0668
Show file tree
Hide file tree
Showing 35 changed files with 70 additions and 0 deletions.
2 changes: 2 additions & 0 deletions 01-data/01-lesson/01-01-lesson.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,8 @@ ggplot(email50, aes(x = num_char, y = exclaim_mess, color = factor(spam))) +

*Based on the plot, does there appear to be a relationship between these variables?*

## Submit```{r encoder_ui, echo=FALSE}hash_encoder_ui = { shiny::div("If you have completed this tutorial and are happy with all of your", "solutions, please enter your identifying information, then click the button below to generate your hash", textInput("name", "What's your name?"), textInput("studentID", "What is your student ID?"), renderText({ input$caption }), )}``````{r encoder_logic, echo=FALSE, context="server"}is_server_context = function(.envir) { # We are in the server context if there are the follow: # * input - input reactive values # * output - shiny output # * session - shiny session # # Check context by examining the class of each of these. # If any is missing then it will be a NULL which will fail. inherits(.envir$input, "reactivevalues") & inherits(.envir$output, "shinyoutput") & inherits(.envir$session, "ShinySession")}check_server_context = function(.envir) { if (!is_server_context(.envir)) { calling_func = deparse(sys.calls()[[sys.nframe()-1]]) err = paste0( "Function `", calling_func,"`", " must be called from an Rmd chunk where `context = \"server\"`" ) stop(err, call. = FALSE) }}encoder_logic = function(strip_output = FALSE) { p = parent.frame() check_server_context(p) # Make this var available within the local context below assign("strip_output", strip_output, envir = p) # Evaluate in parent frame to get input, output, and session local({ encoded_txt = shiny::eventReactive( input$hash_generate, { # shiny::getDefaultReactiveDomain()$userData$tutorial_state state = learnr:::get_tutorial_state() shiny::validate(shiny::need(length(state) > 0, "No progress yet.")) shiny::validate(shiny::need(nchar(input$name) > 0, "No name entered.")) shiny::validate(shiny::need(nchar(input$studentID) > 0, "Please enter your student ID")) user_state = purrr::map_dfr(state, identity, .id = "label") user_state = dplyr::group_by(user_state, label, type, correct) user_state = dplyr::summarize( user_state, answer = list(answer), timestamp = dplyr::first(timestamp), .groups = "drop" ) user_state = dplyr::relocate(user_state, correct, .before = timestamp) user_info = tibble(label = c("student_name", "student_id"), type="identifier", answer= as.list(c(input$name, input$studentID)), timestamp=format(Sys.time(), "%Y-%m-%d %H:%M:%S %Z", tz="UTC")) learnrhash::encode_obj(bind_rows(user_info, user_state)) } ) output$hash_output = shiny::renderText(encoded_txt()) }, envir = p)}encoder_logic()``````{r encode, echo=FALSE}learnrhash::encoder_ui(ui_before = hash_encoder_ui)```

## Congratulations!

You have successfully completed Lesson 1 in Tutorial 1: Introduction to data.
Expand Down
2 changes: 2 additions & 0 deletions 01-data/02-lesson/01-02-lesson.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,8 @@ One potential reason for this paradox is that women tended to apply to competiti

Note that we were only able to discover the contradictory finding once we incorporated information about the department of the application. Examples like this highlight the importance of a good study design that considers and collects information on extraneous, but potentially confounding variables in a study.

## Submit```{r encoder_ui, echo=FALSE}hash_encoder_ui = { shiny::div("If you have completed this tutorial and are happy with all of your", "solutions, please enter your identifying information, then click the button below to generate your hash", textInput("name", "What's your name?"), textInput("studentID", "What is your student ID?"), renderText({ input$caption }), )}``````{r encoder_logic, echo=FALSE, context="server"}is_server_context = function(.envir) { # We are in the server context if there are the follow: # * input - input reactive values # * output - shiny output # * session - shiny session # # Check context by examining the class of each of these. # If any is missing then it will be a NULL which will fail. inherits(.envir$input, "reactivevalues") & inherits(.envir$output, "shinyoutput") & inherits(.envir$session, "ShinySession")}check_server_context = function(.envir) { if (!is_server_context(.envir)) { calling_func = deparse(sys.calls()[[sys.nframe()-1]]) err = paste0( "Function `", calling_func,"`", " must be called from an Rmd chunk where `context = \"server\"`" ) stop(err, call. = FALSE) }}encoder_logic = function(strip_output = FALSE) { p = parent.frame() check_server_context(p) # Make this var available within the local context below assign("strip_output", strip_output, envir = p) # Evaluate in parent frame to get input, output, and session local({ encoded_txt = shiny::eventReactive( input$hash_generate, { # shiny::getDefaultReactiveDomain()$userData$tutorial_state state = learnr:::get_tutorial_state() shiny::validate(shiny::need(length(state) > 0, "No progress yet.")) shiny::validate(shiny::need(nchar(input$name) > 0, "No name entered.")) shiny::validate(shiny::need(nchar(input$studentID) > 0, "Please enter your student ID")) user_state = purrr::map_dfr(state, identity, .id = "label") user_state = dplyr::group_by(user_state, label, type, correct) user_state = dplyr::summarize( user_state, answer = list(answer), timestamp = dplyr::first(timestamp), .groups = "drop" ) user_state = dplyr::relocate(user_state, correct, .before = timestamp) user_info = tibble(label = c("student_name", "student_id"), type="identifier", answer= as.list(c(input$name, input$studentID)), timestamp=format(Sys.time(), "%Y-%m-%d %H:%M:%S %Z", tz="UTC")) learnrhash::encode_obj(bind_rows(user_info, user_state)) } ) output$hash_output = shiny::renderText(encoded_txt()) }, envir = p)}encoder_logic()``````{r encode, echo=FALSE}learnrhash::encoder_ui(ui_before = hash_encoder_ui)```

## Congratulations!

You have successfully completed Lesson 2 in Tutorial 1: Introduction to data.
Expand Down
2 changes: 2 additions & 0 deletions 01-data/03-lesson/01-03-lesson.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,8 @@ quiz(
```


## Submit```{r encoder_ui, echo=FALSE}hash_encoder_ui = { shiny::div("If you have completed this tutorial and are happy with all of your", "solutions, please enter your identifying information, then click the button below to generate your hash", textInput("name", "What's your name?"), textInput("studentID", "What is your student ID?"), renderText({ input$caption }), )}``````{r encoder_logic, echo=FALSE, context="server"}is_server_context = function(.envir) { # We are in the server context if there are the follow: # * input - input reactive values # * output - shiny output # * session - shiny session # # Check context by examining the class of each of these. # If any is missing then it will be a NULL which will fail. inherits(.envir$input, "reactivevalues") & inherits(.envir$output, "shinyoutput") & inherits(.envir$session, "ShinySession")}check_server_context = function(.envir) { if (!is_server_context(.envir)) { calling_func = deparse(sys.calls()[[sys.nframe()-1]]) err = paste0( "Function `", calling_func,"`", " must be called from an Rmd chunk where `context = \"server\"`" ) stop(err, call. = FALSE) }}encoder_logic = function(strip_output = FALSE) { p = parent.frame() check_server_context(p) # Make this var available within the local context below assign("strip_output", strip_output, envir = p) # Evaluate in parent frame to get input, output, and session local({ encoded_txt = shiny::eventReactive( input$hash_generate, { # shiny::getDefaultReactiveDomain()$userData$tutorial_state state = learnr:::get_tutorial_state() shiny::validate(shiny::need(length(state) > 0, "No progress yet.")) shiny::validate(shiny::need(nchar(input$name) > 0, "No name entered.")) shiny::validate(shiny::need(nchar(input$studentID) > 0, "Please enter your student ID")) user_state = purrr::map_dfr(state, identity, .id = "label") user_state = dplyr::group_by(user_state, label, type, correct) user_state = dplyr::summarize( user_state, answer = list(answer), timestamp = dplyr::first(timestamp), .groups = "drop" ) user_state = dplyr::relocate(user_state, correct, .before = timestamp) user_info = tibble(label = c("student_name", "student_id"), type="identifier", answer= as.list(c(input$name, input$studentID)), timestamp=format(Sys.time(), "%Y-%m-%d %H:%M:%S %Z", tz="UTC")) learnrhash::encode_obj(bind_rows(user_info, user_state)) } ) output$hash_output = shiny::renderText(encoded_txt()) }, envir = p)}encoder_logic()``````{r encode, echo=FALSE}learnrhash::encoder_ui(ui_before = hash_encoder_ui)```

## Congratulations!

You have successfully completed Lesson 3 in Tutorial 1: Introduction to data.
Expand Down
2 changes: 2 additions & 0 deletions 01-data/04-lesson/01-04-lesson.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ ggplot(evals, aes(x = bty_avg, y = score, color = cls_type)) +
geom_point()
```

## Submit```{r encoder_ui, echo=FALSE}hash_encoder_ui = { shiny::div("If you have completed this tutorial and are happy with all of your", "solutions, please enter your identifying information, then click the button below to generate your hash", textInput("name", "What's your name?"), textInput("studentID", "What is your student ID?"), renderText({ input$caption }), )}``````{r encoder_logic, echo=FALSE, context="server"}is_server_context = function(.envir) { # We are in the server context if there are the follow: # * input - input reactive values # * output - shiny output # * session - shiny session # # Check context by examining the class of each of these. # If any is missing then it will be a NULL which will fail. inherits(.envir$input, "reactivevalues") & inherits(.envir$output, "shinyoutput") & inherits(.envir$session, "ShinySession")}check_server_context = function(.envir) { if (!is_server_context(.envir)) { calling_func = deparse(sys.calls()[[sys.nframe()-1]]) err = paste0( "Function `", calling_func,"`", " must be called from an Rmd chunk where `context = \"server\"`" ) stop(err, call. = FALSE) }}encoder_logic = function(strip_output = FALSE) { p = parent.frame() check_server_context(p) # Make this var available within the local context below assign("strip_output", strip_output, envir = p) # Evaluate in parent frame to get input, output, and session local({ encoded_txt = shiny::eventReactive( input$hash_generate, { # shiny::getDefaultReactiveDomain()$userData$tutorial_state state = learnr:::get_tutorial_state() shiny::validate(shiny::need(length(state) > 0, "No progress yet.")) shiny::validate(shiny::need(nchar(input$name) > 0, "No name entered.")) shiny::validate(shiny::need(nchar(input$studentID) > 0, "Please enter your student ID")) user_state = purrr::map_dfr(state, identity, .id = "label") user_state = dplyr::group_by(user_state, label, type, correct) user_state = dplyr::summarize( user_state, answer = list(answer), timestamp = dplyr::first(timestamp), .groups = "drop" ) user_state = dplyr::relocate(user_state, correct, .before = timestamp) user_info = tibble(label = c("student_name", "student_id"), type="identifier", answer= as.list(c(input$name, input$studentID)), timestamp=format(Sys.time(), "%Y-%m-%d %H:%M:%S %Z", tz="UTC")) learnrhash::encode_obj(bind_rows(user_info, user_state)) } ) output$hash_output = shiny::renderText(encoded_txt()) }, envir = p)}encoder_logic()``````{r encode, echo=FALSE}learnrhash::encoder_ui(ui_before = hash_encoder_ui)```

## Congratulations!

You have successfully completed all of the lessons in Tutorial 1: Introduction to data.
Expand Down
2 changes: 2 additions & 0 deletions 02-explore/01-lesson/02-01-lesson.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,8 @@ ggplot(pies_updated, aes(y = flavor)) +
```


## Submit```{r encoder_ui, echo=FALSE}hash_encoder_ui = { shiny::div("If you have completed this tutorial and are happy with all of your", "solutions, please enter your identifying information, then click the button below to generate your hash", textInput("name", "What's your name?"), textInput("studentID", "What is your student ID?"), renderText({ input$caption }), )}``````{r encoder_logic, echo=FALSE, context="server"}is_server_context = function(.envir) { # We are in the server context if there are the follow: # * input - input reactive values # * output - shiny output # * session - shiny session # # Check context by examining the class of each of these. # If any is missing then it will be a NULL which will fail. inherits(.envir$input, "reactivevalues") & inherits(.envir$output, "shinyoutput") & inherits(.envir$session, "ShinySession")}check_server_context = function(.envir) { if (!is_server_context(.envir)) { calling_func = deparse(sys.calls()[[sys.nframe()-1]]) err = paste0( "Function `", calling_func,"`", " must be called from an Rmd chunk where `context = \"server\"`" ) stop(err, call. = FALSE) }}encoder_logic = function(strip_output = FALSE) { p = parent.frame() check_server_context(p) # Make this var available within the local context below assign("strip_output", strip_output, envir = p) # Evaluate in parent frame to get input, output, and session local({ encoded_txt = shiny::eventReactive( input$hash_generate, { # shiny::getDefaultReactiveDomain()$userData$tutorial_state state = learnr:::get_tutorial_state() shiny::validate(shiny::need(length(state) > 0, "No progress yet.")) shiny::validate(shiny::need(nchar(input$name) > 0, "No name entered.")) shiny::validate(shiny::need(nchar(input$studentID) > 0, "Please enter your student ID")) user_state = purrr::map_dfr(state, identity, .id = "label") user_state = dplyr::group_by(user_state, label, type, correct) user_state = dplyr::summarize( user_state, answer = list(answer), timestamp = dplyr::first(timestamp), .groups = "drop" ) user_state = dplyr::relocate(user_state, correct, .before = timestamp) user_info = tibble(label = c("student_name", "student_id"), type="identifier", answer= as.list(c(input$name, input$studentID)), timestamp=format(Sys.time(), "%Y-%m-%d %H:%M:%S %Z", tz="UTC")) learnrhash::encode_obj(bind_rows(user_info, user_state)) } ) output$hash_output = shiny::renderText(encoded_txt()) }, envir = p)}encoder_logic()``````{r encode, echo=FALSE}learnrhash::encoder_ui(ui_before = hash_encoder_ui)```

## Congratulations!

You have successfully completed Lesson 1 in Tutorial 2: Summarizing and visualizing data.
Expand Down
2 changes: 2 additions & 0 deletions 02-explore/02-lesson/02-02-lesson.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,8 @@ allow_retry = TRUE
)
```

## Submit```{r encoder_ui, echo=FALSE}hash_encoder_ui = { shiny::div("If you have completed this tutorial and are happy with all of your", "solutions, please enter your identifying information, then click the button below to generate your hash", textInput("name", "What's your name?"), textInput("studentID", "What is your student ID?"), renderText({ input$caption }), )}``````{r encoder_logic, echo=FALSE, context="server"}is_server_context = function(.envir) { # We are in the server context if there are the follow: # * input - input reactive values # * output - shiny output # * session - shiny session # # Check context by examining the class of each of these. # If any is missing then it will be a NULL which will fail. inherits(.envir$input, "reactivevalues") & inherits(.envir$output, "shinyoutput") & inherits(.envir$session, "ShinySession")}check_server_context = function(.envir) { if (!is_server_context(.envir)) { calling_func = deparse(sys.calls()[[sys.nframe()-1]]) err = paste0( "Function `", calling_func,"`", " must be called from an Rmd chunk where `context = \"server\"`" ) stop(err, call. = FALSE) }}encoder_logic = function(strip_output = FALSE) { p = parent.frame() check_server_context(p) # Make this var available within the local context below assign("strip_output", strip_output, envir = p) # Evaluate in parent frame to get input, output, and session local({ encoded_txt = shiny::eventReactive( input$hash_generate, { # shiny::getDefaultReactiveDomain()$userData$tutorial_state state = learnr:::get_tutorial_state() shiny::validate(shiny::need(length(state) > 0, "No progress yet.")) shiny::validate(shiny::need(nchar(input$name) > 0, "No name entered.")) shiny::validate(shiny::need(nchar(input$studentID) > 0, "Please enter your student ID")) user_state = purrr::map_dfr(state, identity, .id = "label") user_state = dplyr::group_by(user_state, label, type, correct) user_state = dplyr::summarize( user_state, answer = list(answer), timestamp = dplyr::first(timestamp), .groups = "drop" ) user_state = dplyr::relocate(user_state, correct, .before = timestamp) user_info = tibble(label = c("student_name", "student_id"), type="identifier", answer= as.list(c(input$name, input$studentID)), timestamp=format(Sys.time(), "%Y-%m-%d %H:%M:%S %Z", tz="UTC")) learnrhash::encode_obj(bind_rows(user_info, user_state)) } ) output$hash_output = shiny::renderText(encoded_txt()) }, envir = p)}encoder_logic()``````{r encode, echo=FALSE}learnrhash::encoder_ui(ui_before = hash_encoder_ui)```

## Congratulations!

You have successfully completed Lesson 2 in Tutorial 2: Summarizing and visualizing data.
Expand Down
Loading

0 comments on commit 08a0668

Please sign in to comment.