The following documentation assumes you have basic notions of how to build a RShiny app. Follow the documentations of RShiny and Mastering Shiny for more explanations, and for some practical examples.
Installation
You can install the development version of SingleCellViz in the folder of your choice like so:
or alternatively create a new R project using the GUI of RStudio (by clicking on the top-right R icon in a blue box), which is the Project menu. Select the “Version Control” way of creating a project, then “Git”, and finally type in the repository url: https://github.com/metzger-chambon/singlecellviz.git.
In either case, you will be working inside a R project. Check out the
doc
about using R projects. An important thing to remember when
developing is that you should always have the root of this R project as
you working directory (getwd()
and setwd()
).
Moreover, everything you refer to while developing should be relative to
your working directory.
R environment
You should first check that you have the same version of R (also
written at the very top of renv.lock
file).
R.version.string
#> [1] "R version 4.4.1 (2024-06-14)"
You will need to install the required packages and their dependencies. This can easily be done using renv R package, by running the following:
renv::init()
# Select Answer 1: Restore the project from the lockfile.
Check out renv documentation and section Using renv for more information on how this works.
You can also find necessary packages and suggested packages in the
DESCRIPTION file (respectively under Imports
field and
Suggests
field). In particular, suggested packages are
useful when developing the app.
Running the app
Once you have your local version of the app, you can test your
development version easily by running the code in
dev/run_dev.R
.
Notably, it runs the golem wrapper run_app()
. The
default database to run the app has only one very small dataset,
corresponding to a subset of pbmc3k dataset. If you want to test it on
other datasets, check out the Database
vignette.
As the default dataset is very small, it is good practice to also test the app on a bigger dataset, to make sure that it is not lagging when scaling up.
General structure
Generalities
The app follows the structure proposed by the R package golem and their associated book on developing production-grade Shiny Apps.
This impacts the structure of the app, as it means that:
- the app is built around the notion of submodule and utility functions
- the app is built as an R package (also check out package development doc)
Description of the repository
Here is the general structure of the repository:
├── app.R # file with script to launch the app when deployed on the server (RShiny)
├── data # folder containing datasets available in the package (R packages)
├── DESCRIPTION # file with metadata (R packages)
├── dev # folder with scripts to run when developping the app (golem)
├── inst # folder with arbitrary additional files (R packages)
├── LICENSE.md # LICENSE of the app (R package)
├── man # folder with documentation of the package (R packages)
├── NAMESPACE # file with import/export to make package self-contained (R packages)
├── NEWS.md # file to summarize updates made to the package (R packages)
├── _pkgdown.yml # file with configuration of pkgdown (pkgdown)
├── presentation # folder containing pdf presentation of the app
├── R # folder with all R code (R packages)
├── README.md # README file (R packages)
├── renv # folder with renv libraries (renv)
├── renv.lock # file with name and version of packages (renv)
├── singlecellviz.Rproj # defines the R project of the app
├── tests # folder containing automatic tests (testthat)
└── vignettes # folder containing documentation (R packages)
Description of R/
scripts
Here is a description of scripts developed for the app:
-
app_config.R
contains function for golem usage -
app_ui.R
andapp_server.R
contains respectively the ui and server main code of the app (this is where each module function is called) -
mod_*.R
contains independent code parts of the app, there is one for each tab of the app, and for some other features like choosing the dataset or downloading it -
fct_*.R
contains useful common functions for example for handling plots, tables, or for adding new datasets -
golem_utils_*.R
contains general utility functions relative to ui or server handling -
R6.R
contains the definition of the DATA class (see section COMMON_DATA variable) -
run_app.R
contains golem wrapper ofshinyApp()
function to run the app.
Create a new module
To create a new module, run
golem::add_module(name = "your_new_module_name", with_test = TRUE)
,
it will create the R scripts in the R/
folder, as well as
tests in the tests
folder (see golem
doc for more information).
You can add module_*_ui()
and
module_*_server()
functions respectively to
app_ui.R
and app_server.R
.
Follow golem doc and Shiny doc for more technical information on how to develop modules.
In this app, there are some objects you might need when developing a new module. The following sections (studies variable, r variable and COMMON_DATA variable) describe these objects and their usage.
studies variable
Modules might need to have access to some summary information of all
datasets. studies
is the data.frame object for that. It is
structured like so:
structure(list(title = "small example",
output = "extdata/small_example/tiledb",
rds = "extdata/small_example/object.rds",
description = "Dataset of Peripheral Blood Mononuclear Cells (PBMC) subsetted for Mk and DC cells. Freely available from 10X Genomics.",
doi = "10.1038/ncomms14049",
date = "16/01/2017",
nsamples = 1L,
nfeatures = 100L,
ncells = 46L),
class = "data.frame", row.names = c(NA, -1L))
As you can notice, it has a similar structure than the file given as
input of run_app()
(described in Database vignette). This is because the input
file given to run_app()
is read, and saved in the variable
studies
.
To get access to it in a module just add the line of code in your module:
studies <- get_golem_options("studies")
For more information about how this works, check out golem doc.
r variable
Modules might have the r
object as parameter. It is a
global reactiveValues (see Shiny
and golem
doc). It is a special list with elements:
-
tabs
, the name of the current tab in use -
selected_study
, the title of the current selected study
It is a way to share information across otherwise independent modules. The particularity of reactiveValues objects is that every time the object gets updated, the parts of code that are dependent on them get recomputed. So you might need to use:
-
r$selected_study
if the output of a function is dependent on the dataset -
r$tabs
if you only want to update an output when on a specific tab
COMMON_DATA variable
Modules might have the COMMON_DATA
object as parameter.
It is a R6 object (see R6 and golem
doc).
It contains the public methods:
-
output
, the uri of the current study -
title
, the title of the current study -
tabs_updated
, a list with elements name being the name of a tab, and their value being the number (positional value) of latest study it has been updated with
and the active methods:
-
experiment
, returns an opened SOMA -
groups
, returns the name of available groups, which would be the equivalent of Seurat Assays (e.g. RNA) -
arrays
, returns the name of available arrays, which would be the equivalent of Seurat slots (e.g. count) -
markers
, returns the name of availablemarkers
tables -
comparison
, returns the name of availablecomparison
tables
The particularity of a R6 object is that if it modified inside of a
function, the modified value is kept outside the function when it is
called. So if you develop a module and need some data to be passed to
other modules, without the need for reactivity, you will likely have to
use the COMMON_DATA
object.
Good pratices
To develop the app efficiently, there are some good practice that need to be followed. They are presented in the following sections.
They are all explained in more details in the golem documentation, but the following sections describe the major points to keep in mind when developping.
Using git
Introduction
Git is a version control system used to track changes made to the code over time. It is helpful to:
- revert/compare with previous versions
- understand who made changes and why (with commit messages)
- manage conflicts when various changes were made to the code
It is different from github, which is a git platform service to create, store and manage code. This is the place where you can share code to collaborate, as well as create, assign and track issues, bugs and feature requests.
Usage in practice
You can pull and push new versions of the app on github, if you have the appropriate user rights.
When you locally make a change in a file and want to apply the changes on github, you need to run the commands (via the Terminal, in the local folder of the app) in the following order:
-
git add $FILE
: add the changes in the file -
git commit -m $MESSAGE
: commit the changes -
git push
: push commits on github (you must have the rights to do so on the repository)
When doing a lot of changes, it is good practice to create a new
branch (git branch $BRANCHNAME
), and work on it
(git checkout $BRANCHNAME
). When all changes are made and
working, you can merge the main and new branch
(git checkout master
then,
git merge $BRANCHNAME
). Read the following good
practice post on git branches.
Also, read the related golem documentation on git.
Using renv
renv is a R package to create reproducible environments for R projects. Each R project has its own independent collection of packages with specific versions.
Basically:
- to check if
renv.lock
is up-to-date, runrenv::status()
- to restore an environment from the current
renv.lock
, runrenv::restore()
- to update it, run
renv::snapshot()
Create a new version release
You’ve made changes to the app, and it should be released as a new version of the app? Follow (at least) these steps:
- run
run_dev()
one last time and make sure that the whole app works as expected, also on bigger datasets - document the changes made in the code and vignettes
- update the DESCRIPTION file with
attachment::att_amend_desc()
if needed - run
devtools::check()
, and make sure you don’t have any errors. One warning about the size of files insinglecellviz/inst/extdata/small_example/tiledb/
is yet not resolved. It is not a problem as long as the package does not go on CRAN. - run
pkgdown::build_site()
to update the site with the new documentation, and check that looks ok
Finally, with git:
- add and commit your last changes
- follow best practice for merging to a release branch
- run
golem::set_golem_version("X.X.X")
with your new version numbers - describe changes in
NEWS.md
- follow best practice for merging to the master branch
- push your new release to github
- give it a tag (
git tag -a X.X.X -m "X.X.X"
) - push the tag (
git push origin X.X.X
) - merge the release tag to develop
(
git merge develop X.X.X
)
Golem doc provides some more things to check checklist.