Set the global exam configuration. Must be called from the server context (i.e., in an R code chunk with context="server").

exam_config(
  auth_provider,
  storage_provider,
  exercise_data_provider,
  exercise_evaluator,
  seed_attempt,
  points_format,
  cache_data = FALSE
)

data_provider(fun, cache_data = FALSE)

Arguments

auth_provider

an authentication provider function to identify a user. See authentication providers for details. If NULL, the authentication provider will be reset to a dummy provider which generates a random user id for every request.

storage_provider

a storage provider list of functions for saving/retrieving section data. See storage providers for details. If NULL, the authentication provider will be reset to a dummy provider which does not safe any section data.

exercise_data_provider

a data provider function which generates the user-specific (e.g., randomized) data for all exercise chunks. Similar to data_provider, but gets an argument chunk_label instead of section. See the section on Exercise Data Providers below. Note that exercise chunks have access only to the data returned by this data provider. The enclosing environment of the returned environment (or list) will be set to the empty environment, emptyenv().

exercise_evaluator

a function which creates an exercise evaluator. See exercise evaluators for details.

seed_attempt

a function to generate the seed for the random number generator used in a single attempt. By default every user gets their unique seed which does not change between attempts. See Seeding function for more details.

points_format

formats for showing the number of points per question. The first format is for plural, the second for singular. If only a single format is given, it is used for both plural and singular. Can also be a function which receives the number of points and should return the label to display. The default is points_format = c('%d point', '%d points').

cache_data

cache the data generated by the exercise data provider (for exam_config()) or the data provider for (data_provider()) in the Shiny session. This makes sense if generating the data takes a while, but is only small in size.

fun

a data provider function which generates the user-specific (e.g., randomized) data for the sections. See section Data provider for details.

Details

Only non-missing options are set, the others are kept at their previous values.

Data provider

Each section and question is rendered in a sterile environment, containing only the data returned by the data provider. This data provider is called every time a section is rendered and should return a list or environment with all the objects required to render the section. This includes the user-specific randomized values. The function should not set the seed itself, as it is set before the data provider is invoked.

The data provider is a function taking arguments

section

the identifier of the current section or TRUE when data for all sections is requested.

attempt

the attempt object containing information about the current attempt, or NULL if called when pre-rendering the exam.

session

the Shiny session object or NULL if called when pre-rendering the exam.

...

further arguments for future additions

The data provider function returns a list or an environment containing all objects required to render the sections. Internally, the markdown code for each section is rendered with rmarkdown::render() in the environment returned by the data provider.

Warning: To ensure the answers can be reproduced, the exercise data provider must always return the same values for a given attempt. The data provider must not use the attempt$user$user_id information for randomization and should in general not change the seed. When grading the user_id field may not be the same as for the exam itself!

Exercise Data Providers

The setup and user code of an exercise is run in a separate environment, possibly in a different R process. This environment consists only of the objects in the list (or environment) returned by the exercise data provider. The function should not set the seed itself, as it is set before the data provider is invoked. The exercise data provider is run in the server R process and is called with the following arguments:

label

the exercise chunk label

attempt

the attempt object as described in the attempt object section containing information about the current attempt (or NULL, see below).

session

the Shiny session object or NULL for building the auto-completion (see below).

...

further arguments for future additions

If the exercise data provider returns an environment, the enclosing environment will be set to the empty environment, emptyenv().

The exercise data provider is called once when the Shiny server starts with attempt=NULL and session=NULL to ensure auto-completion knows about all available objects.

Warning: To ensure the answers can be reproduced, the exercise data provider must always return the same values for a given attempt & exercise! The data provider must not use the attempt$user$user_id field for randomization, but rely on the seed which is already set. When grading the user_id field may not be the same as for the exam itself!

Seeding function

For more fine-grained control over the random seed used for an attempt, a seeding function may optionally be provided as seed_attempt. This function is called with the following arguments

user

a user object as returned by the authentication provider.

previous_attempts

a list of all previous attempts (see the section on the attempt object). The list does not follow any particular order.

...

further arguments for future additions

The seeding function is useful, for example, if a group of users should get the same seed, or if a user should get the same seed for multiple attempts.

The following seeding function, for example, gives the same seed for all users whose id starts with the same letter. The seed changes with the attempt, but all users in a group will get the same seed for their 2nd, 3rd, etc, attempt.

seeding_function <- function (user, previous_attempts, ...) {
  group <- substr(user$user_id, 1, 1)
  # take the number of attempts into account
  digest::digest2int(paste(group, length(previous_attempts)))
}

The next seeding function gives a different seed for every user, but gives the same seed for every 5 attempts.

seeding_function <- function (user, previous_attempts, ...) {
  blocks_of_5 <- floor(length(previous_attempts) / 5)
  digest::digest2int(paste(user$user_id, blocks_of_5))
}

Attempt object

Some functions receive one or more attempt objects. An attempt object is a list with the following elements:

id

a character string (UUID) uniquely identifying the attempt.

user

a user object as returned by the authentication provider.

seed

an integer associated with the attempt for initializing the random number generator.

started_at

the UTC time the attempt was first started (as POSIXct).

See also

Other exam configuration: section_config()

Examples

# Use the RStudio Connect user and assign a fixed seed for all attempts function (shiny_session) { user_id <- shiny_session$user return(list(user_id = user_id, seed = digest::digest2int(shiny_session$user))) }
#> function (shiny_session) { #> user_id <- shiny_session$user #> return(list(user_id = user_id, seed = digest::digest2int(shiny_session$user))) #> } #> <environment: 0x556ef3dbc058>
# Use the RStudio Connect user and assign the same seed for all users with the same first letter. function (shiny_session) { user_id <- shiny_session$user return(list(user_id = user_id, seed = function (user, prev_attempts, ...) { })) }
#> function (shiny_session) { #> user_id <- shiny_session$user #> return(list(user_id = user_id, #> seed = function (user, prev_attempts, ...) { #> #> })) #> } #> <environment: 0x556ef3dbc058>