ReviewR manages database connections with the help of the DBI and dbplyr packages from RStudio. Leveraging these packages, ReviewR is able to operate on remote databases as if they are local, in-memory data frames. R code written with dplyr verbs are automatically translated to SQL by the dbplyr package and sent to the connected database. The translation to the connected database is handled by a DBI connection object, which references a database driver. A full list of dplyr compatible database back-ends can be found here.
ReviewR facilitates these DBI connections through the use of Shiny Modules. Modules can make complex Shiny applications more manageable. They compartmentalize code and are developed for a specific purpose.
At their core, modules monitor user inputs that are defined in a User Interface (UI) function, process these inputs in a server function, and return an output. Each module operates in its own namespace, meaning that Shiny modules are portable and can be used in other applications or reused within the same application.
In ReviewR, this concept is used to create “database modules.” Each patient database back-end will have different connection requirements, meaning a new UI is needed to collect the required connection information from the user. By modularizing this connection code, ReviewR can support multiple databases so long as a correctly formatted DBI connection object is returned to the ReviewR application. Thus “database modules” are Shiny modules that are responsible for collecting appropriate database credentials (IP address, port, etc.) and returning a DBI connection object.
ReviewR dynamically switches between developed database modules with its own database setup module. This setup module allows users to interactively switch between database modules that have been developed. When selected, each individual module’s UI will be presented to guide the user through establishing a database connection.
Users can extend the functionality of ReviewR by adding developed
database modules to the “Database Module Setup” code chunk within
R/mod_database_setup.R
as shown below:
# Database Module Setup ----
<- 'db-selector-ns'
namespace # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Add Database Setup Modules Here!!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
## Add Database Setup Modules Here
<- reactiveValues(bigquery = bigquery_setup_server(id = namespace ),
database_setup_vars postgresql = postgresql_setup_server(id = namespace ) #,
# new_module = new_module_setup_server(id = namespace)
)# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
Once modules are added to the database setup, they will appear in the Patient Database Module drop down on the setup tab.
Read on to learn how to develop a database module for ReviewR!
Creating a Shiny module is very similar to developing a stand alone Shiny Application. When developing a database module for ReviewR, there are a few differences that result because of its package architecture. A template has been provided so that some aspects of ReviewR database module development can be highlighted in the sections that follow.
If you are familiar with Shiny modules and want to begin developing right away, you may skip ahead to the Start Developing Section of this guide.
The first step to developing a new ReviewR module will be to fork the latest version of ReviewR from GitHub. Forking the repository will allow you to experiment with the ReviewR package and ensures you have access to the latest development tools and file structure of the ReviewR package. For simplicity, the home directory is specified as a destination directory:
# install.packages('usethis')
::create_from_github(repo_spec = 'thewileylab/ReviewR', destdir = '~/') usethis
Please see this guide if you have trouble connecting RStudio with Git. It is also possible to fork this repository directly from GitHub if all else fails.
Navigate to the forked repository and open the
ReviewR.Rproj
file. This will open the project in RStudio.
Finally, using the devtools package run:
# install.packages('devtools')
::load_all() devtools
This will load the most recent version of ReviewR. At this point, you are ready to begin developing a database module.
A helper function, dev_database_module()
, has been
provided with ReviewR to help jump start the database module development
process. This function accepts 2 arguments:
mod_name
- This is an internal variable that ReviewR
uses to keep track of the return values from the module, including the
DBI connection object.display_name
- This variable will be the “choice” that
is presented to users on the Setup tab, so be sure it is descriptive and
recognizable!When ready, run:
:::dev_database_module(mod_name = 'your_module', display_name = 'Your Module Display Name') ReviewR
This will create a module file in the R/
directory of
the ReviewR package and open it for editing.
Take note of the ReviewR Database Module Template. It consists of 2 functions:
ReviewR utilizes the moduleServer()
function introduced in Shiny 1.5. This allows the server portion of a
module to be called within your application just like any other
function, instead of relying on the previous callModule()
syntax.
The next thing to note is that ReviewR modules will ultimately become a part of the ReviewR package namespace. This means that both the UI and Server functions require roxygen comments, with all function parameters, returns, and necessary package imports declared.
For additional information on module development, please see: https://shiny.rstudio.com/articles/modules.html
{module_name}_setup_ui
that
accepts a single parameter (id){module_name}_setup_server
with a reactiveValues()
return
Begin by adding Shiny
Widgets to the tagList()
portion of the UI function in
your database module. Users will interact with these widgets to enter
their database credentials. Consider adding a “Connect” and “Disconnect”
button to your UI which can be monitored by your server function to
store the user entered credentials and combine them into a DBI
connection object. Specifics will vary by database. Remember to wrap all
user inputID’s with the ns()
function, so that they will
inherit the module namespace.
tagList(
actionButton(inputId = ns('connect'),label = 'connect'),
actionButton(inputId = ns('disconnect'),label = 'disconnect')
)
As mentioned previously, the entire goal of a ReviewR database module
is to construct a DBI connection object with user entered information.
This DBI connection object needs to be assigned to the
db_con
reactive values object, say when a user clicks a
button labeled ‘connect’. However, this user entered information should
be validated. Consider the following pseudo code:
library(shiny)
## Observe a connect button, defined in the UI function
observeEvent(input$connect, {
## When the button is pressed, store a temporary connection
<- DBI::dbConnect(drv = database_driver(),
temp_con user = input$username, ## A text input, with a username
pass = input$password ## A password input
)## Design a test to validate the temporary connection object
<- connection_test(temp_con)
test_result
## If the test passes, update the reactiveValues objects for the `is_connected` and `db_con` variables
if(test_result == TRUE) {
$is_connected <- 'yes'
database_export$db_con <- temp_con
database_export## Else, don't create connection info
else {
} $is_connected <- 'no'
database_export
}
})## Let ReviewR handle the rest!
The database setup module in ReviewR is ‘observing’ the return of the
currently selected database module. When the is_connected
variable changes to ‘yes’, it knows that valid connection
information is present, and it can begin working with the connected
database.
Database modules are also responsible for clearing connection information when a user wants to disconnect. Here is a simple example of a database disconnect where a user presses a ‘disconnect’ button:
## Observe a connect button, defined in the UI function
observeEvent(input$disconnect, (
## Tell the database you are disconnecting
::dbDisconnect(database_export$db_con)
DBI## Clear the connection info
$db_con <- NULL
database_export## Set `is_connected` variable back to 'no'
$is_connected <- 'no'
database_export ))
Think about how users will be interacting with this module. When a button is clicked, how will they notice that changes are being processed? Will an error message be displayed if incorrect credentials are entered? When connected, will a “connection success” message be presented?
As you are developing/testing, remember to add your completed
setup_server
function to
R/mod_database_setup.R
. This is required to have the module
display within ReviewR. When incorporated into ReviewR, the
moduleName
parameter of the database module’s
reactiveValues will be what appears in the Database Module Selector drop
down list. Once you are sure you have added your module to
mod_database_setup.R
and you are ready to test, run:
::document_and_reload() golem
This will incorporate your newly developed module into the ReviewR namespace, allowing you to see any changes that you have made as a result of your development process.
Once you are satisfied with your module and have sufficiently tested its functionality, run:
::install() devtools
This will install a new, custom version of the ReviewR package with your newly developed module incorporated.
# UI ----
#' {display_name} Setup UI
#'
#' This module is designed to guide a user through the process of authenticating your database
#'
#' @param id The module namespace
#'
#' @return The {display_name} Setup UI
#'
#' @importFrom shiny NS
#'
<- function(id) {
{mod_name}_setup_ui <- NS(id)
ns tagList(
## UI widgets here ----
)
}
# Server ----
#' {display_name} Setup Server
#'
#' @param id The Module namespace
#'
#' @return {display_name} connection variables
#' @export
#'
#' @importFrom DBI dbConnect
#'
<- function(id) {
{mod_name}_setup_server moduleServer(
id,function(input, output, session) {
<- session$ns
ns ## {mod_name} Export Values ----
<- reactiveValues(
{mod_name}_export ### Module Info
moduleName = '{display_name}',
moduleType = 'database',
setup_ui = ReviewR::{mod_name}_setup_ui,
is_connected = 'no',
db_con = NULL
)# Server Code Here ----
# Return ----
return({mod_name}_export)
}
) }