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, andZZZ
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)
.
- Example: To update the default dose amount for model 1, regimen 1,
it would be:
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 inmodel_switch_conditions()
.
-
code_premable
andcode_postamble
are boilerplate code to remind users the model should be in character form surrounded by double quotation marks, and to assign it to themodel_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 byzero_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.