source('www/includes.R')
You've made it! This is our final tutorial. Here, we show you how to incorporate your shinymgr project into your own R package or deploy it on a server, where your users can run the purpose-driven apps that you created. Generally speaking, deployment entails divorcing from the shinymgr development framework that have been the focus of previous tutorials.
👉🏾In most cases, you will maintain your shinymgr project as a separate project from the package or server deployment so that you can continue to upgrade your shinymgr project (database and all) independent from other deployments.
To illustrate the deployment process, a new shinymgr demo project was setup when you launched the tutorial. As a friendly reminder, a shinymgr directory named "shinymgr" contains the following folder structure.
fs::dir_tree( path = paste0(tempdir(), "/shinymgr"), recurse = FALSE )
Of these, a few folders are no longer needed for deployment.
👉🏼The remaining directories are needed; they simply need to be copied to the correct place in your package or server environment.
We'll begin by describing how to add your shinymgr project to your own package, and then discuss how to deploy it on a server.
Your shinymgr project can be incorporated into your own package, where the "apps" you build help users with analyses that are relevant to your package.
If you have not developed an R package before, we strongly recommend that you read through the R packages book by Hadley Wickham and Jenny Bryan at https://r-pkgs.org/. Here, we will create the simplest of packages and show you how to copy the shinymgr demo project into it.
👉🏾If you want a fully functioning package: Before you proceed, read this article on the Posit helppage https://support.posit.co/hc/en-us/articles/200486498 and make sure you have the necessary tools for package development. This step involves installing new software onto your computer if you are a Windows user, and checking to make sure you've done it correctly. You may need to run R in administrator mode.
👉🏼Note: If you have a second instance of RStudio running, you may wish to create a new package by creating a new project and following along with the RStudio prompts.
We will use an R package called devtools to help with package creation.
library(devtools)
Our package will create two tiny functions, f()
and g()
, which have two arguments name "x" and "y" that are numeric. One function will add "x" and "y", while the other will subtract "y" from "x".
f <- function(x, y) x + y g <- function(x, y) x - y
Now, let's use the package.skeleton()
function in base R to create a package that includes both functions, along with R's built-in datasets named "cars".
package.skeleton( path = tempdir(), name = "myPackage", list = c("f", "g", "cars"), force = TRUE )
Let's look at what is included in the package directory itself.
library(fs) fs::dir_tree( path = paste0(tempdir(), "/myPackage"), recurse = TRUE)
Notice there are several folders and files here:
This is the file structure of a bare-bones package. The functions f()
and g()
have been added to the "R" folder.
Next, we will remove all files except the DESCRIPTION, as we'll soon recreate them with devtools functions.
# remove some original NAMESPACE file file.remove( paste0(tempdir(), "/myPackage/NAMESPACE") ) # remove the Read-and-delete-me file file.remove( paste0(tempdir(), "/myPackage/Read-and-delete-me") ) # remove original R functions f and g file.remove( list.files( paste0(tempdir(), "/myPackage/R"), full.names = TRUE ) ) # remove original documentation in the man folder file.remove( list.files( paste0(tempdir(), "/myPackage/man"), full.names = TRUE ) )
Now, let's re-create the two original .R function files so that they include roxygen comments, which are used to create the function's help files, NAMESPACE (and more).
f_text <- c( "#' @title Add two numbers", "#' @param x An integer or numeric value.", "#' @param y An integer or numeric value.", "#' @return A numeric vector.", "#' @export", "#' @examples", "#' f(x = 3, y = 2)", "#' f(x = 1.6, y = 2.4)", "f <- function(x, y) {x + y}" ) g_text <- c( "#' @title Subtract two numbers", "#' @param x An integer or numeric value.", "#' @param y An integer or numeric value.", "#' @return A numeric vector.", "#' @export", "#' @examples", "#' f(x = 3, y = 2)", "#' f(x = 1.6, y = 2.4)", "g <- function(x, y) {x - y}" )
We'll use the writeLines()
function to create the R scripts for the two functions:
# write the f.R file writeLines( con = paste0(tempdir(), "/myPackage/R/f.R"), text = f_text ) # write the g.R file writeLines( con = paste0(tempdir(), "/myPackage/R/g.R"), text = g_text )
Let's have a look at the f()
function file that was produced:
cat( paste( readLines(paste0(tempdir(), "/myPackage/R/f.R")), collapse = '\n' ) )
This is a bare-bones example of an R function. The script begins with roxygen comments that define the function's help page and dependencies, followed by the function's code.
If you are following along in a second instance of R, you should also remove the functions "f" and "g" from your global environment if present, as they are now tucked safely into the package's R directory and will cause conflicts when trying to check the package.
# remove the functions from your global environment rm(list = "f", "g")
Next, we use devtools document()
function to document the package. This function will create the .Rd files (R documentation) that are stored in the package's "man" folder, and will further write the NAMESPACE file.
devtools::document(pkg = paste0(tempdir(), "/myPackage"))
Now that our package is documented, we can run the check()
function to check if the package passes initial tests. (We won't actually run this in the tutorial to save time).
devtools::check( pkg = "myPackage", manual = TRUE)
Hopefully, if you are following along, the R CMD check results show 0 errors and 0 warnings.
Now that we have a package in place, we are ready to incorporate the shinymgr demo project into your package. The package, "myPackage", will contain 2 functions ("f" and "g") plus a shiny application called "myApp" (which is the demo shinymgr master app).
👉🏾We realize the functions, "f" and "g", along with the RData file "cars" have nothing to do with the shinymgr demo modules. Bear with us. The main idea is that you would write your own modules and create shinymgr apps that highlight functions in your own package.
When you launched this tutorial, the shinymgr demo project was created in the tutorial's temporary directory. Additionally, myPackage was created in the temporary directory as well. Thus, the tutorial's temporary directory contains a folder called "shinymgr", and a second folder called "myPackage". We will be moving files from the "shinymgr" directory to the "myPackage" directory in this section, and then running package tests once again.
Here are the tasks:
The first few steps are simple. Just use the file.copy()
function to copy items in the "data" directory in shinymgr over to myPackage.
# copy the shinymgr data into the package file.copy( from = paste0(tempdir(), "/shinymgr/data"), to = paste0(tempdir(), "/myPackage/"), recursive = TRUE )
After this step, the package file structure should look like this:
fs::dir_tree( path = paste0(tempdir(), "/myPackage"), recurse = TRUE)
The actual shiny app should be stored in the "inst" folder of the R package. Here, we will create the directory "inst", and further add a subdirectory called "myApp". Once files are copied over to the package, they can be modified (don't modify them in your shinymgr development project).
# create the myApp directory dir.create( path = paste0(tempdir(), '/myPackage/inst/myApp'), recursive = TRUE )
Next, let's copy over the shinymgr modules, modules_app, modules_mgr, and reports directory to the package.
# get the filepaths for the folders of interest shinymgr_dirs <- paste0( tempdir(), "/shinymgr/", c('modules', 'modules_app', 'modules_mgr', 'reports') ) # loop through folders and copy them into the package for (i in 1:length(shinymgr_dirs)) { file.copy( from = shinymgr_dirs[i], to = paste0(tempdir(), "/myPackage/inst/myApp"), recursive = TRUE ) }
Next, let's copy over the ui, global, and server R scripts that provide the master shiny app:
# list the main shiny R files shiny_files <- c("ui.R", "global.R", "server.R") # copy the files into the package for (i in 1:3) { file.copy( from = paste0(tempdir(), "/shinymgr/", shiny_files[i]), to = paste0(tempdir(), "/myPackage/inst/myApp") ) }
During development of your shinymgr project, to see a list of apps that can be run, the database is queried and will display only apps that are active. When you deploy your shinymgr apps in a package, ensure that the modules_app folder contains only those app scripts that are active and featured in your deployment; the reliance on the database will be broken.
To sever this dependence on the database, open the deployment copy of the new_analysis.R script (which is found in the "modules_mgr" directory), delete lines 35-43, and un-comment line 48.
35 >> # Get a list of available apps (from the database) 36 >> analysis_list <- sort(qry_row( 37 >> 'apps', 38 >> list( 39 >> appActive = 1 40 >> ), 41 >> 'pkAppName', 42 >> shinyMgrPath 43 >> )$pkAppName)
44 >> 45 >> # Get a list of available apps (from the directory contents) 46 >> # NOTE: Un-comment the below code (and delete the code above) after development 47 >> # is complete to remove the dependency on the shinymgr sqlite database. 48 >> # analysis_list <- sort(tools::file_path_sans_ext(dir('modules_app')))
By deleting lines 36-43 and un-commenting the code on line 48, the database dependency is removed.
In the deployed version, we next remove the developer tools from the ui.R and server.R scripts.
To remove the developer tools from ui.R, first delete lines 9-11 to remove the "Developer Portal" header.
9 >> hr(), 10 >> tags$h3("Developer Portal", style = "text-align:center"), 11 >> hr(),
You may also consider modifying lines 16 and 21 so that they no longer have the "beta" label.
16 >> text = "Analysis (beta)",
16 >> text = "Analysis",
$\vdots$
21 >> text = "Reports (beta)",
21 >> text = "Reports",
Next, delete lines 25-29 to remove the Developer Tools option in the sidebar menu. Be sure to remove only the comma at the end of line 24 to avoid errors when launching the app.
24 >> ), 25 >> menuItem( 26 >> text = "Developer Tools", 27 >> tabName = "DevTools", 28 >> icon = icon("wrench") 29 >> )
Finally, delete lines 48-82 to remove the Developer Tools tab. Again, remove only the comma at the end of line 46 to avoid errors when launching the app.
46 >> ), 47 >> 48 >> # developer Tools 49 >> tabItem( 50 >> tabName = "DevTools", 51 >> tabsetPanel( 52 >> id = "dev_tool_tabs", 53 >> 54 >> # builder goes in first tab 55 >> tabPanel( 56 >> title = "Build App", 57 >> value = "build_tab", 58 >> uiOutput("build_app") 59 >> ), 60 >> 61 >> # database tab 62 >> tabPanel( 63 >> title = "Database", 64 >> value = "shinymgr_db", 65 >> uiOutput("my_db_output") 66 >> ), 67 >> 68 >> # queries 69 >> tabPanel( 70 >> title = "Queries", 71 >> value = "query_db", 72 >> uiOutput("query_output") 73 >> ), 74 >> 75 >> # tab for adding reports 76 >> tabPanel( 77 >> title = "Add Report", 78 >> value = "add_report_tab", 79 >> uiOutput("add_report_output") 80 >> ) 81 >> ) # end tabsetPanel 82 >> ) # end of developer tools tabItem
The ui.R script should now only contain tabs for new analyses and new reports.
Next, we have to modify the server.R script so that it only loads the new_analysis
and new_report
modules. The final server.R script should just look like this:
source("global.R") server <- function(input, output, session) { # call the new_analyses module ui ----------------------------- output$new_analysis <- renderUI({ new_analysis_ui("new_analysis") }) new_analysis_server( id = "new_analysis", tabSelect = reactive({input$tabs}), shinyMgrPath = shinyMgrPath ) # call the new_report module ui ----------------------------- output$new_report <- renderUI({ new_report_ui("new_report") }) new_report_server( id = "new_report" ) } # end of server function
We have now removed all the developer tools from the app, and so their corresponding modules can also be deleted.
# list the main shiny R files dev_modules_files <- c( "add_app.R", "add_mod.R", "add_report.R", "add_tab.R", "app_builder.R", "my_db.R", "queries.R", "stitch_script.R", "table.R" ) # delete these files that in the package for (i in 1:length(dev_modules_files)) { file.remove( paste0( tempdir(), "/myPackage/inst/myApp/modules_mgr/", dev_modules_files[i] ) ) }
After removing the dependency on the shinymgr database and Developer Tools, the library call to shinymgr in line 6 of global.R is no longer needed. Instead, it can be replaced with the shiny, shinydashboard, and shinyjs packages.
5 >> # load required shiny framework packages 6 >> library(shinymgr)
5 >> # load required shiny framework packages 6 >> library(shiny) 7 >> library(shinydashboard) 8 >> library(shinyjs)
It is useful to write a function that your users will call to launch "myApp". Here, we create the file "launch_myApp.R", which will contain roxygen documentation and the function code. The function itself will contain no arguments . . . when the function is called, it will launch "myApp".
launch_text <- c( "#' @title Run the myApp shiny example", "#' @export", "#' @examples", "#' launch_myApp()", "launch_myApp <- function() {", "appDir <- system.file(", " 'myApp', ", " package = 'myPackage'", ")", "shiny::runApp(appDir, display.mode = 'normal')", "}" ) # create the function file and add to the package writeLines( con = paste0(tempdir(), "/myPackage/R/launch_myApp.R"), text = launch_text )
Let's have a look at the code that was written:
cat( paste( readLines(paste0(tempdir(), "/myPackage/R/launch_myApp.R")), collapse = '\n' ) )
The final step is to add the packages used in the app as package dependencies to the DESCRIPTION file. This means that users of "myPackage" will need to have shiny, shinyjs, and shinydashboard on their machine. When "myPackage" is loaded, shiny, shinyjs, and shinydashboard will also be loaded. Similary, we should add the testthat package as a "Suggests" package, as this package is needed to run the coding tests.
dependencies <- c( "Imports: ", " shiny,", " shinyjs,", " shinydashboard,", "Suggests:", " testthat" ) cat(dependencies, file = paste0(tempdir(), "/myPackage/DESCRIPTION"), sep = "\n", append = TRUE)
The DESCRIPTION file now reads:
cat( paste( readLines(paste0(tempdir(), "/myPackage/DESCRIPTION")), collapse = '\n' ) )
At this point, your shinymgr project has been migrated over to a package (specifically, "myPackage"). Now, let's run the document()
function again.
devtools::document(pkg = paste0(tempdir(), "/myPackage"))
Next, we'll use the dir_tree()
function from the package, fs, to show the full contents of "myPackage."
library(fs) fs::dir_tree( path = paste0(tempdir(), "/myPackage"), recurse = TRUE)
This shows the full layout of a package that includes a shinymgr project. Note the launch_myApp()
function is included in the R directory and the shiny app is included in a folder called "myApp", inside the "inst" folder.
👉🏻Your package is complete. Now, when you compile your package for distribution, your user should be able to load your package, and launch the shiny app.
library(myPackage) myPackage::launch_myApp()
Another way to deploy your shinymgr project is on a server. There are many options for doing this (Shiny Server, RStudio Connect), but here will discuss deployment on shinyapps.io, a self-service platform that makes it easy to share your shiny applications on the web. The service runs in the cloud on shared servers that are operated by RStudio, and has both free and paid plans.
👉🏾 Please see https://docs.posit.co/shinyapps.io/ for full details.
Many tasks are common between including a shinymgr project in a package and deploying a shinymgr app on shinyapps.io. To deploy a shinymgr project on shinyapps.io, the following tasks should be completed:
First, install rsconnect
and create an account on shinyapps.io (instructions can be found here).
Then follow steps 1-5 from the [Incorporating shinymgr] section.
Finally, follow the instructions from Posit to publish MyApp on shinyapps.io.
Congratulations! You should now be able to access and share your shinymgr app on shinyapps.io!
We've briefly discussed how to include shinymgr into your own projects.
👉🏼 If you’d like a pdf of this document, use the browser “print” function (right-click, print) to print to pdf. If you want to include quiz questions and R exercises, make sure to provide answers to them before printing.
You're finished! Really finished!
If you would like to contribute to shinymgr, please visit our GitLab website at https://code.usgs.gov/vtcfwru/shinymgr, the canonical home of shinymgr. There, you can clone our repository, and make a "pull request" ("merge request") if you would like us to incorporate your new features to shinymgr. We also welcome "issues", where you can post code bugs, tutorial bugs, etc.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.