# rtemisseq
# ::rtemislive::
# 2024 EDG rtemis.org
# shiny::runApp("./")
# Setup
library(rtemis)
library(rtemisbio)
library(shiny)
library(bslib)
library(htmltools)
library(plotly)
source("globals.R")
source("data.R")
# Colors
primary <- "#72CDF4"
info <- helpcol <- "#FEB2E0"
success <- "#B4DC55"
#' Create protein visualization shinylive app
#'
#' Visualize protein amino acid sequence and annotations using
#' `dplot3_protein()`
#'
#' Set verbosity to 1 to monitor app progress
#'
#' @param default_theme Character: "dark" or "light" theme
#' @param verbosity Integer: 0 = silent, 1 = verbose
#'
#' @author EDG
#' @export
#' @return A shiny app that can be converted to a shinylive app
rtemisseq_version <- "0.2.6"
seqvizlive <- function(
default_theme = "dark",
protein_plotly_height = "900px",
jsonedit_height = "900px",
verbosity = 0) {
# Logo
logo <- base64enc::dataURI(
file = "./www/rtemisbio_gray.png", mime = "image/png"
)
# Version
platform <- sessionInfo()[["platform"]]
svl <- paste0(
"rtemisseq v.", rtemisseq_version,
" | ", "rtemis v.0.97.3", utils::packageVersion("rtemis"), # doesn't work in shinylive
" | ", "rtemisbio v.", utils::packageVersion("rtemisbio"),
" | R v.", version$major, ".", version$minor,
" | running on ", platform
)
# Shinylive info
shinylive_info <- if (substr(platform, 1, 4) == "wasm") {
paste0(
"
This application has been compiled to ",
as.character(a("WebAssembly", href = "https://webassembly.org/", target = "_blank")),
" using ",
as.character(a("shinylive", href = "https://posit-dev.github.io/r-shinylive/", target = "_blank")),
"
and is best viewed with the latest version of Chrome."
)
} else {
NULL
}
# UI page_navbar ----
ui <- function(request) {
bslib::page_navbar(
# Title ----
title = list(
logo = a(
img(
src = logo,
width = "140px",
height = "auto",
alt = "rtemisbio"
),
href = "https://rtemis.org/rtemisbio",
)
),
id = "rtemisbio",
selected = "Welcome",
footer = span(
svl,
" © 2024 EDG",
style = "display: block; text-align: center; margin-top: 1em; margin-bottom: 1em;"
),
# Theme ----
theme = bslib::bs_theme(
bg = "#fff",
fg = "#000",
primary = primary,
secondary = "#704071",
success = success,
info = info,
`tooltip-bg` = "#303030",
`tooltip-color` = helpcol,
`tooltip-opacity` = 1,
`tooltip-border-radius` = "10px",
`tooltip-padding-x` = "1rem",
`tooltip-padding-y` = "1rem",
`tooltip-font-size` = "1rem"
# base_font = bslib::font_google("Inter"), # doesn't work in shinylive
# code_font = bslib::font_google("Fira Code")
) |>
bs_add_rules(sass::sass_file("www/rtemislive.scss")),
# Window title ----
window_title = "rtemisSeq",
# Language ----
lang = "en",
# tags$script(src = "globals.js"),
# Nav Panels ----
## [] Welcome ----
bslib::nav_panel(
title = "Welcome",
icon = bsicons::bs_icon("stars"),
bslib::card(
h4("Welcome to rtemisSeq", style = "text-align: center;"),
card_body(
class = "d-inline text-center",
HTML(paste0(
"rtemisSeq is a web interface for ",
as.character(a("rtemisbio", href = "https://rtemis.org/rtemisbio", target = "_blank")),
",
providing interactive visualization of sequence data.",
"
Created for the ", as.character(a("FTD CWOW", href = "https://cwow.ucsf.edu/", target = "_blank")),
".
",
rthelp_inline(
"To get started, use the navigation tabs at the top.",
title = "Welcome"
),
shinylive_info
)),
br(), br(),
bslib::card_image(
file = "./www/svl.webp",
alt = "rtemislive",
align = "center",
border_radius = "all",
fill = FALSE,
width = "40%",
class = "mx-auto"
)
)
)
), # /nav_panel Welcome
## [] Protein Visualization ----
bslib::nav_panel(
title = "Protein Visualization",
icon = bsicons::bs_icon("body-text"),
card(
full_screen = TRUE,
class = "p-0",
# allow items side by side ----
card_header(
class = "d-flex justify-content-end",
# tooltip UI output ----
uiOutput("ui_a3_tooltip"),
# popover UI output ----
uiOutput("ui_a3_popover")
),
layout_sidebar(
fillable = TRUE,
# Data Load Sidebar ----
sidebar = bslib::sidebar(
uiOutput("ui_a3_load_switch"), # Switch between file upload and built-in data
uiOutput("ui_a3_data_load"), # File upload or built-in data selection depending on switch
uiOutput("ui_a3_data_info"), # Shows dataset info
uiOutput("ui_a3_plot_button"), # Click to plot
# uiOutput("ui_a3_tooltip")
),
# Plot Output ----
# for shinylive, do not use plotlyOutput directly in ui !!
# fails when no plot is rendered, unlike shiny
uiOutput("ui_dplot3_protein")
)
)
# bslib::navset_card_tab(
# full_screen = TRUE,
# # []] Sequence Viewer ----
# bslib::nav_panel(
# title = "Sequence Viewer",
# layout_sidebar(
# # Data Load Sidebar ----
# sidebar = bslib::sidebar(
# uiOutput("ui_a3_load_switch"), # Switch between file upload and built-in data
# uiOutput("ui_a3_data_load"), # File upload or built-in data selection depending on switch
# uiOutput("ui_a3_data_info"), # Shows dataset info
# uiOutput("ui_a3_plot_button") # Click to plot
# ),
# # Plot Output ----
# # for shinylive, do not use plotlyOutput directly in ui !!
# # fails when no plot is rendered, unlike shiny
# uiOutput("ui_dplot3_protein")
# )
# ),
# # []] jsonedit ----
# bslib::nav_panel(
# title = "Raw Data",
# bslib::card(
# bslib::card_title("Sequence & Annotation data"),
# uiOutput("ui_jsonedit")
# )
# ) # /nav_panel jsonedit
# ) # /navset_card_tab
), # /nav_panel Protein Visualization
## [] About ----
bslib::nav_panel(
title = "About",
icon = bsicons::bs_icon("info-square"),
bslib::card(
card_image(
file = "./www/rtemisbio_s.webp",
href = "https://rtemis.org/rtemisbio",
alt = "rtemislive",
align = "center",
border_radius = "all",
fill = FALSE,
width = "54%",
class = "mx-auto"
),
div(
class = "d-inline text-center",
HTML(paste0(
"Created by the ",
as.character(a("FTD CWOW", href = "https://cwow.ucsf.edu/", target = "_blank")),
" Genomics & Transcriptomics core.
",
"Powered by rtemis & rtemisbio (",
as.character(a("rtemis.org", href = "https://rtemis.org", target = "_blank")),
")."
)),
br(), br(),
a(
img(
src = "rtemis_gray.png",
alt = "rtemis",
align = "center",
width = "190px"
),
href = "https://rtemis.org",
target = "_blank"
),
align = "center"
)
)
), # /nav_panel About
bslib::nav_spacer(),
bslib::nav_item(input_dark_mode(id = "dark_mode", mode = default_theme)),
header = list(
# busy indicators ----
shinybusy::add_busy_spinner(
spin = "orbit",
# color = "#72CDF4",
color = "#00ffff",
timeout = 200,
position = "bottom-left",
onstart = FALSE
)
)
) # /ui /bslib::page_navbar
} # /ui function
# Server ----
server <- function(input, output, session) {
# UI a3 load switch ----
output$ui_a3_load_switch <- shiny::renderUI({
if (verbosity > 0) {
message("Rendering ui_a3_load_switch")
}
# Radio buttons: built-in data vs upload file ----
shiny::radioButtons(
inputId = "a3_load_switch",
label = "Data source",
choices = list(
`Built-in datasets` = "builtin",
`File upload` = "upload"
),
selected = "builtin"
)
})
# UI a3 Data ----
output$ui_a3_data_load <- shiny::renderUI({
req(input$a3_load_switch)
if (input$a3_load_switch == "upload") {
# Upload a3 JSON file
if (verbosity > 0) {
message("Rendering ui_a3_data_load for file upload")
}
shiny::fileInput(
inputId = "a3_file",
label = "Upload a3 JSON file",
buttonLabel = "Browse local files...",
)
} else {
# Select built-in data stored in ./data/ directory
if (verbosity > 0) {
message("Rendering ui_a3_data_load for built-in data selection")
}
shiny::selectizeInput(
inputId = "a3_builtin_data",
label = "Select built-in a3 dataset",
choices = c("mapt_annot", "mapt_clv"),
selected = "mapt_annot"
)
}
}) # /ui_a3_data_load
# UI for a3 data info ----
output$ui_a3_data_info <- shiny::renderUI({
req(a3_obj())
if (verbosity > 0) {
message("Rendering ui_a3_data_info")
}
bslib::card(
bslib::card_title("a3 Dataset Info", container = htmltools::h6),
bslib::card_body(
# HTML("Sequence length:", paste0("", length(a3_obj()$Sequence), "")),
# if (length(a3_obj()$UniprotID) > 0) {
# HTML("Uniprot ID:", paste0("", a3_obj()$UniprotID, ""))
# },
# if (length(a3_obj()$Reference) > 0) {
# a("Reference", href = a3_obj()$Reference, target = "_blank")
# },
summarize_a3(a3_obj()),
fillable = FALSE
) # /card_body
) # /card
}) # /ui_a3_data_info
# UI for plot action button ----
output$ui_a3_plot_button <- shiny::renderUI({
req(a3_obj())
if (verbosity > 0) {
message("Rendering ui_a3_plot_button")
}
bslib::input_task_button(
"a3_plot_button",
"Plot dataset",
icon = bsicons::bs_icon("magic"),
label_busy = "Drawing...",
icon_busy = bsicons::bs_icon("clock-history"),
type = "primary",
auto_reset = TRUE
)
}) # /ui_a3_plot_button
# Load Dataset ----
a3_obj <- shiny::reactive({
req(input$a3_load_switch)
if (input$a3_load_switch == "upload") {
req(input$a3_file)
if (verbosity > 0) {
message("Loading a3 JSON file '", input$a3_file$datapath, "'")
}
dat <- read.a3json(input$a3_file$datapath)
if (verbosity > 0) {
message("Loaded dataset of class '", class(dat)[1], "'")
}
return(dat)
} else {
# Load built-in data from data.R objects
req(input$a3_builtin_data)
if (verbosity > 0) {
message("Loading built-in a3 dataset '", input$a3_builtin_data, "'")
}
# read.3.json from data folder works fine in shiny app, not shinylive,
# use objects from data.R instead
# assign to dat object of name input$a3_builtin_data
dat <- get(input$a3_builtin_data)
if (verbosity > 0) {
message("Loaded dataset of class '", class(dat)[1], "'")
}
return(dat)
}
}) # /a3_obj
# dplot3 theme is "white" or "black" ----
dplot3_theme <- shiny::reactive({
req(input$dark_mode)
if (input$dark_mode == "dark") {
"black"
} else {
"white"
}
}) # /dplot3_theme
# Render plotly ----
output$dplot3_protein <- plotly::renderPlotly({
req(a3_obj())
if (verbosity > 0) {
message("Rendering dplot3_protein of object with class '", class(a3_obj())[1], "'")
}
plot(
a3_obj(),
theme = dplot3_theme(),
marker.size = input$marker.size,
font.size = input$font.size,
ptm.marker.size = input$ptm.marker.size,
clv.marker.size = input$clv.marker.size,
bg = input$plot.bg, # legend.bg defaults to transparent, this sets paper bg
plot.bg = input$plot.bg,
marker.col = input$marker.col,
n.per.row = if (input$n.per.row == "auto") NULL else as.integer(input$n.per.row)
)
}) |> # /dplot3_protein
bindEvent(input$a3_plot_button, input$a3_plot_update_button)
# Create variable clicked that is TRUE after input$a3_plot_button is clicked
clicked <- shiny::reactiveVal(FALSE)
shiny::observeEvent(input$a3_plot_button, {
clicked(TRUE)
})
# UI a3 tooltip ----
output$ui_a3_tooltip <- shiny::renderUI({
if (clicked()) {
bslib::tooltip(
trigger = span(
"Plot help", bsicons::bs_icon("info-circle", class = "text-info"),
style = "text-align: right;",
class = "rtanihi"
),
div(
# HTML(
# paste0(
# "