Skip to contents

Supplying Passworded Models

Motivation

To avoid re-entering in model code, dosing regimens, and other settings every time, users can provide an additional file to be sourced by MVP during launch. To use this functionality, it is assumed that users have a working knowledge of the Shiny framework.

How models are supplied to MVP

A list of models (stored as character objects) are included by default when MVPapp is installed, from shiny/code_templates.R, within the directory of where MVPapp was installed. This is the list that the users sees in the drop-down list when they select models, and is stored as a vector of characters called model_examples_list. There are two functions related to model selection:

  • model_switch_conditions() pastes the relevant model code into the code editor within the App when the user selects a matching model title from the drop-down list.
  • update_model_choices() stores model-specific information such as dosing regimens, MW, axis labels and scaling (i.e. any Shiny Inputs) associated with a password.

When both of these functions are re-defined by an external R script, additional password-protected models become available in MVP.

An example external file is provided in /shiny/passworded_models_example.R. The user is recommended to copy this script locally and use it as a starting point to insert models, and supply the path for the pw_models_path argument to run_mvp(), i.e. run_mvp(pw_models_path = "path/to/your/private/models.R").

Re-defining model_switch_conditions()

The structure of model_switch_conditions() is relative straightforward - each model is provided as a key-value pair, where the left-hand side (LHS) is the title of the model, and the right-hand side (RHS) is the code to be pasted when the associated title is selected.

  • the LHS title must match exactly with your eventual passworded model’s title.
  • the RHS consists of an object containing mrgsolve model code (in character form, see Section “Supplying Model Code”), and boiler plate postamble code (mcode_model_choice). The postamble code is required to tell apart Model 1 / Model 2 during model compilation.

In the example (passworded_models_example.R), you can see that the first one on the list is called “Test Passworded Model”. Note that this model is not visible when you launch MVP - this behavior is intended, as “Test Passworded Model” is hidden from selection by default from the drop-down list until a valid password has been supplied from the “More…” tab (in this case the password is “test”), in which case the model is “unlocked” and become available for selection. This is the basis of how passworded models work in MVP.

Therefore, the list in model_switch_conditions() must contain every model, both public and private.

Re-defining update_model_choices()

The structure of update_model_choices() looks a bit more complicated, however none of the arguments nor its return value is of importance for the user. The only relevant part is the following:

list_of_valid_passwords <- c("test")

### Example model
if (input_password == "test") {
  create_alert("FirstName LastName", "first.last@xxx.com")
  new_choices <- c('Test Passworded Model', model_examples_list)
  updateXXXInput(session, "YYY", value = ZZZ)
  .
  .
  .
  
} 
  
  • list_of_valid_passwords should be edited to contain a vector of characters that are valid passwords.
  • (input_password == "test") should be edited to contain password(s) associated with the model(s) that the user wishes to be unlocked.
  • create_alert() optional, this creates a pop-up notifying the user that the model is now unlocked. The first name, last name and email should be changed accordingly.
  • new_choices should be edited to insert the matching title(s) associated with the model(s), and added to the original model_examples_list.
  • All following lines included in the condition are used to update Shiny Inputs to default to a new value, where XXX is the type of Input, "YYY" is the id name associated with the Input, and ZZZ is the new default value.
    • Example: To update the default dose amount for model 1, regimen 1, it would be: updateNumericInput(session, "amt1", value = 1000).

As almost all Shiny Inputs can be updated this way and there are far too many to list, the most common Input id names are included below for reference:

Dosing Options

Description Shiny Input Type id
Dose Amount, Regimen 1 (Model 1) updateNumericInput “amt1”
First Dose Time, Regimen 1 (Model 1) updateNumericInput “delay_time1”
Total Doses, Regimen 1 (Model 1) updateNumericInput “total1”
Dosing Interval, Regimen 1 (Model 1) updateNumericInput “ii1”
Infusion Duration, Regimen 1 (Model 1) updateNumericInput “tinf1”
Use MW (Model 1) updateCheckboxInput “mw_checkbox”
MW value (Model 1) updateNumericInput “mw”
MW multiplication factor (Model 1) updateNumericInput “multi_factor”
Use Weight-based dosing (Model 1) updateCheckboxInput “wt_based_dosing_checkbox”
Weight-based parameter name (Model 1) updateTextInput “wt_based_dosing_name”
Model Duration (Model 1) updateCheckboxInput “model_dur_checkbox”
Model Rate (Model 1) updateCheckboxInput “model_rate_checkbox”
  • Dose amount, first dose time, total doses, dosing interval, and infusion duration goes up to 5, i.e. use "amt5" to update the dose amount for regimen 5.
  • For Model 2, append _2 as a suffix to the id name. E.g. total doses for model 2, regimen 3, would be "total3_2"
  • Note: due to the way models are loaded, the Input CMT cannot currently be pre-specified. This also applies to “Select Y-axis”, and dataset options.

Sampling Options

Description Shiny Input Type id
Max Sampling Time updateNumericInput “tgrid_max”
Sampling Frequency updateNumericInput “delta”
Custom Sampling Schedule updateTexInput “custom_sampling_time_text”
Add Sampling at Time 0 updateCheckboxInput “add_time_zero”
  • Custom sampling schedules can be defined inside the script as an object and then referenced accordingly, e.g.:
q1w_sampling <- c(2, 4, 8, 24, 72, 168)
q3w_sampling <- c(2, 4, 8, 24, 72, 168, 336, 504) + 168*6
q1w_repeated <- unlist(lapply(0:5, function(x) q1w_sampling + x * 168)) # end of Q1W sampling
q3w_repeated <- unlist(lapply(0:7, function(x) q3w_sampling + x * 504)) # end of Q3W sampling

updateTextInput(session, "custom_sampling_time_text", value = 'c(q1w_repeated, q3w_repeated)')

Plotting Options

Description Shiny Input Type id
Y-axis label updateTexInput “y_axis_label”
Log Y-axis (Simulation Page) updateCheckboxInput “log_y_axis”
Log Y-axis (PSA Page, Model 1) updateCheckboxInput “log_y_axis_model_1”
Log Y-axis (PSA Page, Model 2) updateCheckboxInput “log_y_axis_model_2”
Log Y-axis (Variability Page) updateCheckboxInput “log_y_axis_iiv”
Scale X-axis updateSelectizeInput “time_unit”
Show Sampling Points [sims] updateCheckboxInput “geom_point_sim_option”
  • For X-axis Inputs, the names are simply switched to x_axis.
  • Scale X-axis is handled slightly differently, see example below to scale to weeks (origin unit is in hours):
updateSelectizeInput(session, "time_unit", selected = '168', options = list(create = TRUE))

Variability Options

Description Shiny Input Type id e.g.
Select Patient Database, Model 1 updateSelectizeInput “db_model_1” selected = “NHANES”
N Subjects, Model 1 updateNumericInput “n_subj_model_1”
Seed, Model 1 updateNumericInput “seed_number_model_1”
Age, Model 1 updateSliderInput “age_db_model_1” min = 0, max = 100, value = c(18, 65)
Weight, Model 1 updateSliderInput “wt_db_model_1” min = 0, max = 150, value = c(0, 150)
Males Percentage, Model 1 updateSliderInput “males_db_model_1” min = 0, max = 100, value = 100, step = 5
  • use model_2 to update Inputs for Model 2.

If the user wishes to update other Inputs that are not listed above, please refer to the source code for "shiny/app.R".

Note: If the additional model(s) do not need to be password-gated, the entire update_model_choices() function can be removed from the external script (i.e. do not need to be re-defined), and only model_switch_conditions() needs to be changed to include the additional models.

Supplying Model Code

The last piece to tie it all together is to supply the actual model code itself. MVP accepts the character form of mrgsolve code, so objects have to be defined in the format of the following:

model_name <- paste0(code_preamble, '

"
  [mrgsolve model code]

"
', code_postamble)
  • model_name refers to the user-defined name for the model, i.e. the R object that will be used on the RHS to match the corresponding LHS title in model_switch_conditions().
  • code_premable and code_postamble are boilerplate code to remind users the model should be in character form surrounded by double quotation marks, and to assign it to the model_object within the code editor. This should always be included unchanged.
  • The actual mrgsolve mode code should be inserted on its own new line, surrounded by double quotation marks ("). All parameters defined under $PARAM will be available on the Simulation page to be adjusted in real-time. OMEGAs and SIGMAs values should still be supplied, as they can be adjusted on the Variability page (note: all sources of variability are ignored by zero_re() on the Simulation page). The user should pay special attention to strip out both single and double quotation marks if it exists in the original code, as that can be a frequent source of error.

Summary

The most ideal process would be to allow saving & opening projects based on a password all within the Shiny UI itself. However, since MVP was not originally developed with a database architecture in mind, the present solution still provides an an acceptable alternative to allow additional models to be supplied externally upon App launch. The main advantage of this approach is that if MVP is deployed and hosted elsewhere (e.g. via Posit Connect), as long as the hosted environment has access to the external models script, the App does not need to be re-deployed to access the updated models.