upload app directory
Browse files- app/js/index.js +3 -0
- app/logic/__init__.R +2 -0
- app/logic/create_sankey.R +104 -0
- app/logic/data_preprocessor.R +71 -0
- app/logic/render_table.R +72 -0
- app/main.R +112 -0
- app/static/css/app.min.css +1 -0
- app/static/favicon.ico +0 -0
- app/styles/main.scss +2 -0
- app/styles/media_query.scss +0 -0
- app/styles/misc.scss +17 -0
- app/styles/toggle_switches.scss +149 -0
- app/view/__init__.R +2 -0
- app/view/browser.R +38 -0
- app/view/lollipop.R +48 -0
- app/view/sankey.R +227 -0
- app/view/table.R +162 -0
app/js/index.js
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
export function maximizeBox() {
|
2 |
+
|
3 |
+
}
|
app/logic/__init__.R
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
# Logic: application code independent from Shiny.
|
2 |
+
# https://go.appsilon.com/rhino-project-structure
|
app/logic/create_sankey.R
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
box::use(
|
3 |
+
data.table[...],
|
4 |
+
stats[na.omit],
|
5 |
+
plotly[plot_ly, layout],
|
6 |
+
)
|
7 |
+
|
8 |
+
#' sankey_preprare_data
|
9 |
+
#' @description
|
10 |
+
#' The purpose of this function is to wrangle the data to such a form that
|
11 |
+
#' it plotly sankey can be made from it.
|
12 |
+
#'
|
13 |
+
#'
|
14 |
+
#' @param dataset A data table coming from preprocess_data function, it has the
|
15 |
+
#' following columns:
|
16 |
+
#' Consequence | given_ref | Allele | var_name | ensembl_id | biotype |
|
17 |
+
#' gene_name | variant_class | chrom | pos | all_kegg_gene_names | refseq_id |
|
18 |
+
#' kegg_paths_id | kegg_paths_name
|
19 |
+
#' @param selected_pathways a data.table of selected pathways for filtering.
|
20 |
+
#' The table has having the following structure:
|
21 |
+
#'
|
22 |
+
#' | pathway | row_i |
|
23 |
+
# 1: Endocytosis 5
|
24 |
+
#'
|
25 |
+
#' @return list of two data.tables and one vector:
|
26 |
+
#' - labels is a table with two columns:
|
27 |
+
#' | label | id |
|
28 |
+
#' COVID-19 | 1 |
|
29 |
+
#'
|
30 |
+
#' - full has the following columns:
|
31 |
+
#' gene_name | kegg_paths_name | var_name | first | second | third
|
32 |
+
#'
|
33 |
+
#' - scores is a vector of values
|
34 |
+
#' @export
|
35 |
+
#'
|
36 |
+
#' @examples
|
37 |
+
sankey_prepare_data <- function(dataset, selected_pathways, selected_genes, is_pathway) {
|
38 |
+
# filter the selected_pathways -> | gene_name | kegg_paths_name |
|
39 |
+
if (!is_pathway) {
|
40 |
+
sankey_data <- dataset[gene_name %in% selected_genes$gene_name, .(gene_name, kegg_paths_name, var_name)]
|
41 |
+
} else {
|
42 |
+
sankey_data <- dataset[kegg_paths_name %in% selected_pathways$pathway, .(gene_name, kegg_paths_name, var_name)]
|
43 |
+
}
|
44 |
+
|
45 |
+
setorder(sankey_data, kegg_paths_name, gene_name)
|
46 |
+
labels_all <- data.table(
|
47 |
+
label = c(sankey_data$kegg_paths_name, sankey_data$gene_name, sankey_data$var_name)
|
48 |
+
)
|
49 |
+
# get all labels their id
|
50 |
+
labels_all[, id := match(labels_all$label, unique(labels_all$label))]
|
51 |
+
labels <- unique(labels_all)
|
52 |
+
|
53 |
+
with_source <- sankey_data[labels, on = c(kegg_paths_name = "label")]
|
54 |
+
with_source <- with_source[labels, on = c(gene_name = "label")]
|
55 |
+
with_source <- with_source[labels, on = c(var_name = "label")]
|
56 |
+
setnames(with_source, old = c("id", "i.id", "i.id.1"), new = c("first", "second", "third"))
|
57 |
+
full <- unique(na.omit(with_source))
|
58 |
+
path_gene <- unique(full[, .(kegg_paths_name, gene_name, first, second)])
|
59 |
+
scores <- c(rle(rleid(c(full$first + full$second)))$lengths, rep(1, nrow(full)))
|
60 |
+
return(list(labels, full, scores))
|
61 |
+
}
|
62 |
+
|
63 |
+
|
64 |
+
|
65 |
+
#' Title
|
66 |
+
#'
|
67 |
+
#' @param labels
|
68 |
+
#' @param sankey_data
|
69 |
+
#'
|
70 |
+
#' @return
|
71 |
+
#' @export
|
72 |
+
#'
|
73 |
+
#' @examples
|
74 |
+
create_sankey <- function(labels, path_gene, gene_variant, scores) {
|
75 |
+
fig <- plot_ly(
|
76 |
+
type = "sankey",
|
77 |
+
orientation = "h",
|
78 |
+
selectedpoints = c(0:10),
|
79 |
+
node = list(
|
80 |
+
label = labels$label,
|
81 |
+
#x = c(rep(0, length(unique(path_gene$kegg_paths_name))), rep(1, length(unique(gene_variant$gene_name))), rep(2, length(unique(gene_variant$var_name)))),
|
82 |
+
y = seq(0, nrow(labels), by = 1),
|
83 |
+
color = "black",
|
84 |
+
pad = 30,
|
85 |
+
thickness = 30,
|
86 |
+
line = list(
|
87 |
+
color = "black",
|
88 |
+
width = 2
|
89 |
+
)
|
90 |
+
),
|
91 |
+
link = list(
|
92 |
+
source = c(path_gene$first, gene_variant$second) - 1,
|
93 |
+
target = c(path_gene$second, gene_variant$third) - 1,
|
94 |
+
value = scores
|
95 |
+
)
|
96 |
+
)
|
97 |
+
|
98 |
+
fig <- fig |> layout(
|
99 |
+
font = list(
|
100 |
+
size = 14
|
101 |
+
)
|
102 |
+
)
|
103 |
+
fig
|
104 |
+
}
|
app/logic/data_preprocessor.R
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
box::use(
|
3 |
+
data.table[...],
|
4 |
+
stats[na.omit]
|
5 |
+
)
|
6 |
+
|
7 |
+
|
8 |
+
#' preprocess_data
|
9 |
+
#' @description
|
10 |
+
#' This function loads the datasets tsvs, merges them and filters the columns
|
11 |
+
#'
|
12 |
+
#'
|
13 |
+
#' @return table with the following columns:
|
14 |
+
#' Consequence | given_ref | Allele | var_name | ensembl_id | biotype |
|
15 |
+
#' gene_name | variant_class | chrom | pos | all_kegg_gene_names | refseq_id |
|
16 |
+
#' kegg_paths_id | kegg_paths_name
|
17 |
+
#' @export
|
18 |
+
#'
|
19 |
+
#' @examples
|
20 |
+
preprocess_data <- function() {
|
21 |
+
data_dir <- file.path("data/")
|
22 |
+
|
23 |
+
variants_tab_full <- fread(file.path(data_dir, "all_variants.annotated.processed.tsv"), na.strings = c(".", "-"))
|
24 |
+
kegg_tab <- fread(file.path(data_dir, "kegg_tab.tsv"))
|
25 |
+
|
26 |
+
variants_tab <- variants_tab_full[, .(Consequence, GIVEN_REF, Allele, Protein_position, HGVSp, Amino_acids, var_name, Gene, BIOTYPE, SOURCE, SYMBOL, VARIANT_CLASS, chrom, pos)]
|
27 |
+
variants_tab <- variants_tab[!is.na(variants_tab$HGVSp)]
|
28 |
+
variants_tab <- variants_tab[SOURCE == "Ensembl"]
|
29 |
+
|
30 |
+
setnames(variants_tab,
|
31 |
+
old = c("Gene", "GIVEN_REF", "BIOTYPE", "SYMBOL", "VARIANT_CLASS", "Consequence", "Protein_position", "Amino_acids"),
|
32 |
+
new = c("ensembl_id", "given_ref", "biotype", "gene_name", "variant_class", "consequence", "protein_pos", "amino_acids"))
|
33 |
+
|
34 |
+
## join the tables
|
35 |
+
tab <- variants_tab[kegg_tab, on = "ensembl_id", nomatch = 0]
|
36 |
+
tab[, `:=`(gene_definition = NULL, SOURCE = NULL, type = NULL)]
|
37 |
+
return(tab)
|
38 |
+
}
|
39 |
+
|
40 |
+
|
41 |
+
|
42 |
+
|
43 |
+
#' preprocess_maf_data
|
44 |
+
#' @description
|
45 |
+
#' This function loads the datasets tsvs, merges them and filters the columns
|
46 |
+
#'
|
47 |
+
#'
|
48 |
+
#' @return table
|
49 |
+
#' @export
|
50 |
+
#'
|
51 |
+
#' @examples
|
52 |
+
preprocess_maf_data <- function() {
|
53 |
+
data_dir <- file.path("data/")
|
54 |
+
kegg_tab <- fread(file.path(data_dir, "kegg_tab.tsv"))[, .(ensembl_id, kegg_paths_name)]
|
55 |
+
kegg_tab <- kegg_tab[ensembl_id != ""]
|
56 |
+
mutations_data <- fread(file.path(data_dir, "brca_tcga_pub2015/data_mutations.txt"), sep = "\t")
|
57 |
+
|
58 |
+
variants_tab <- mutations_data[, .(Hugo_Symbol, Gene, Chromosome, Start_Position, End_Position, Strand, Consequence, Variant_Classification, Variant_Type, Reference_Allele, Tumor_Seq_Allele2, HGVSc, HGVSp, Protein_position, Codons)]
|
59 |
+
setnames(variants_tab,
|
60 |
+
old = c("Gene", "Hugo_Symbol", "Tumor_Seq_Allele2"),
|
61 |
+
new = c("ensembl_id", "gene_name", "Allele")
|
62 |
+
)
|
63 |
+
|
64 |
+
variants_tab <- na.omit(variants_tab)
|
65 |
+
genes_sample <- sample(variants_tab$gene_name, 2000)
|
66 |
+
variants_tab <- variants_tab[gene_name %in% genes_sample]
|
67 |
+
variants_tab[, var_name := paste0(Chromosome, "_", Start_Position, "_", Reference_Allele, "/", Allele)]
|
68 |
+
tab <- variants_tab[kegg_tab, on = "ensembl_id", nomatch = 0, allow.cartesian = TRUE]
|
69 |
+
return(tab)
|
70 |
+
|
71 |
+
}
|
app/logic/render_table.R
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
|
3 |
+
#' render_genes_table
|
4 |
+
#'
|
5 |
+
#' @param all_pathways_tab a data table with a single column e.g.
|
6 |
+
#' | gene_name |
|
7 |
+
#' RPL22
|
8 |
+
#' @param selected a (1-based) vector of indices of selected genes
|
9 |
+
#' - used if we have been using pathways table and now we have switched back to
|
10 |
+
#' genes tab and want to rerender the table, keeping the genes that we selected
|
11 |
+
#' previously
|
12 |
+
#'
|
13 |
+
#' @return rendered DT table
|
14 |
+
#' @export
|
15 |
+
#'
|
16 |
+
#' @examples
|
17 |
+
render_genes_table <- function(gene_tab, selected = NULL) {
|
18 |
+
print(selected)
|
19 |
+
DT::renderDT({
|
20 |
+
|
21 |
+
DT::datatable(
|
22 |
+
gene_tab,
|
23 |
+
escape = FALSE,
|
24 |
+
selection = list(
|
25 |
+
mode = "multiple",
|
26 |
+
selected = selected,
|
27 |
+
target = "row"
|
28 |
+
),
|
29 |
+
options = list(
|
30 |
+
pageLength = 15
|
31 |
+
)
|
32 |
+
)
|
33 |
+
})
|
34 |
+
}
|
35 |
+
|
36 |
+
|
37 |
+
#' render_pathways_table
|
38 |
+
#'
|
39 |
+
#' @param all_pathways_tab a data table with a single column e.g.
|
40 |
+
#' | pathway |
|
41 |
+
#' Covid-19
|
42 |
+
#' @param selected a (1-based) vector of indices of selected pathways
|
43 |
+
#' - used if we have been using gene table and now we have switched back
|
44 |
+
#' to pathways and want to rerender the table, keeping the pathways that
|
45 |
+
#' we selected previously
|
46 |
+
#'
|
47 |
+
#' @return rendered DT table
|
48 |
+
#' @export
|
49 |
+
#'
|
50 |
+
#' @examples
|
51 |
+
render_pathways_table <- function(all_pathways_tab, selected = NULL) {
|
52 |
+
print(selected)
|
53 |
+
DT::renderDT({
|
54 |
+
tab <- DT::datatable(
|
55 |
+
all_pathways_tab,
|
56 |
+
escape = FALSE,
|
57 |
+
selection = list(
|
58 |
+
mode = "multiple",
|
59 |
+
selected = selected,
|
60 |
+
target = "row"
|
61 |
+
),
|
62 |
+
callback = htmlwidgets::JS('
|
63 |
+
table.on("mouseover", "td", function() {
|
64 |
+
var index = table.cell(this).index();
|
65 |
+
Shiny.setInputValue("table-hover_pathway_genes", index, {priority: "event"});
|
66 |
+
})'),
|
67 |
+
options = list(pageLength = 15)
|
68 |
+
)
|
69 |
+
|
70 |
+
return(tab)
|
71 |
+
})
|
72 |
+
}
|
app/main.R
ADDED
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
box::use(
|
2 |
+
shiny[bootstrapPage, div, icon, moduleServer, fluidRow, NS, renderUI, tags, uiOutput],
|
3 |
+
bs4Dash[box, dashboardPage, tabItems, tabItem, sidebarMenu, menuItem, dashboardHeader, column, dashboardBrand, dashboardSidebar, dashboardBody],
|
4 |
+
thematic[thematic_shiny],
|
5 |
+
shinyjs[useShinyjs, runjs],
|
6 |
+
gargoyle[init]
|
7 |
+
)
|
8 |
+
|
9 |
+
box::use(
|
10 |
+
app/logic/data_preprocessor[preprocess_data],
|
11 |
+
app/logic/data_preprocessor[preprocess_maf_data]
|
12 |
+
)
|
13 |
+
|
14 |
+
box::use(
|
15 |
+
app/view/table,
|
16 |
+
app/view/sankey,
|
17 |
+
app/view/lollipop,
|
18 |
+
app/view/browser
|
19 |
+
)
|
20 |
+
|
21 |
+
thematic_shiny()
|
22 |
+
|
23 |
+
#' @export
|
24 |
+
ui <- function(id) {
|
25 |
+
ns <- NS(id)
|
26 |
+
dashboardPage(
|
27 |
+
header = dashboardHeader(
|
28 |
+
title = "VarWiz",
|
29 |
+
skin = "dark",
|
30 |
+
status = "white"
|
31 |
+
),
|
32 |
+
dark = NULL,
|
33 |
+
help = NULL,
|
34 |
+
sidebar = dashboardSidebar(
|
35 |
+
collapsed = TRUE,
|
36 |
+
status = "navy",
|
37 |
+
skin = "dark",
|
38 |
+
sidebarMenu(
|
39 |
+
id = "side_menu",
|
40 |
+
div(class = "menu-tab", menuItem("Visualization", tabName = "visualization_tab", icon = icon("chart-line"))),
|
41 |
+
div(class = "menu-tab", menuItem("Browse data", tabName = "browse_tab", icon = icon("table"))),
|
42 |
+
div(class = "menu-tab", menuItem("About", tabName = "about_tab", icon = icon("book-open"))),
|
43 |
+
div(class = "menu-tab", menuItem("Start Tutorial", tabName = "tutorial", icon = icon("play")))
|
44 |
+
)
|
45 |
+
),
|
46 |
+
body = dashboardBody(
|
47 |
+
tags$head(
|
48 |
+
tags$script(
|
49 |
+
"$(function() {
|
50 |
+
$('[data-card-widget=\"maximize\"]').on('click', function() {
|
51 |
+
setTimeout(function() {
|
52 |
+
var isMaximized = $('html').hasClass('maximized-card');
|
53 |
+
if (isMaximized) {
|
54 |
+
$('#app-sankey-sankey').css('height', '1000px');
|
55 |
+
} else {
|
56 |
+
$('#app-sankey-sankey').css('height', '400px');
|
57 |
+
}
|
58 |
+
}, 300);
|
59 |
+
$('#app-sankey-sankey').trigger('resize');
|
60 |
+
});
|
61 |
+
});
|
62 |
+
"
|
63 |
+
)
|
64 |
+
),
|
65 |
+
|
66 |
+
useShinyjs(),
|
67 |
+
tabItems(
|
68 |
+
tabItem(
|
69 |
+
tabName = "visualization_tab",
|
70 |
+
fluidRow(
|
71 |
+
column(
|
72 |
+
width = 9,
|
73 |
+
sankey$ui(ns("sankey")),
|
74 |
+
lollipop$ui(ns("lollipop"))
|
75 |
+
),
|
76 |
+
column(
|
77 |
+
width = 3,
|
78 |
+
table$ui(ns("table")),
|
79 |
+
),
|
80 |
+
)
|
81 |
+
),
|
82 |
+
|
83 |
+
tabItem(
|
84 |
+
tabName = "browse_tab",
|
85 |
+
browser$ui(ns("browser"))
|
86 |
+
|
87 |
+
),
|
88 |
+
|
89 |
+
tabItem(
|
90 |
+
tabName = "about_tab"
|
91 |
+
)
|
92 |
+
)
|
93 |
+
|
94 |
+
|
95 |
+
|
96 |
+
)
|
97 |
+
)
|
98 |
+
}
|
99 |
+
|
100 |
+
#' @export
|
101 |
+
server <- function(id) {
|
102 |
+
moduleServer(id, function(input, output, session) {
|
103 |
+
init("reset_sankey")
|
104 |
+
|
105 |
+
# data <- preprocess_data()
|
106 |
+
data <- preprocess_maf_data()
|
107 |
+
browser$server("browser", data)
|
108 |
+
selected_features <- table$server("table", data)
|
109 |
+
selected_gene_rval <- sankey$server("sankey", data, selected_features()[[1]], selected_features()[[2]], selected_features()[[3]])
|
110 |
+
lollipop$server("lollipop", data, selected_gene_rval)
|
111 |
+
})
|
112 |
+
}
|
app/static/css/app.min.css
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
.toggle-switch{position:relative;display:inline-block;width:40px;height:24px;margin:10px}.toggle-switch .toggle-input{display:none}.toggle-switch .toggle-label{position:absolute;top:0;left:0;width:40px;height:24px;background-color:#ccc;border-radius:34px;cursor:pointer;transition:background-color .3s}.toggle-switch .toggle-label::before{content:"";position:absolute;width:20px;height:20px;border-radius:50%;top:2px;left:2px;background-color:#fff;box-shadow:0px 2px 5px 0px rgba(0,0,0,.3);transition:transform .3s}.toggle-switch .toggle-input:checked+.toggle-label{background-color:#2196f3}.toggle-switch .toggle-input:checked+.toggle-label::before{transform:translateX(16px)}.toggle-switch.light .toggle-label{background-color:#bebebe}.toggle-switch.light .toggle-input:checked+.toggle-label{background-color:#9b9b9b}.toggle-switch.light .toggle-input:checked+.toggle-label::before{transform:translateX(6px)}.toggle-switch.dark .toggle-label{background-color:#4b4b4b}.toggle-switch.dark .toggle-input:checked+.toggle-label{background-color:#717171}.toggle-switch.dark .toggle-input:checked+.toggle-label::before{transform:translateX(16px)}.feat-radio-container{display:flex;justify-content:center;align-items:center}.feat-radio-container>h4{margin-top:6px}.toggle-switch-feat{position:relative;display:inline-block;width:40px;height:24px;margin:10px}.toggle-switch-feat .toggle-input-feat{display:none}.toggle-switch-feat .toggle-label-feat{position:absolute;top:0;left:0;width:40px;height:24px;background-color:#bbb;border-radius:34px;cursor:pointer;transition:background-color .3s}.toggle-switch-feat .toggle-label-feat::before{content:"";position:absolute;width:20px;height:20px;border-radius:50%;top:2px;left:2px;background-color:#fff;box-shadow:0px 2px 5px 0px rgba(0,0,0,.3);transition:transform .3s}.toggle-switch-feat .toggle-input-feat:checked+.toggle-label-feat{background-color:#bbb}.toggle-switch-feat .toggle-input-feat:checked+.toggle-label-feat::before{transform:translateX(16px)}.toggle-switch-feat.light .toggle-label-feat{background-color:#bebebe}.toggle-switch-feat.light .toggle-input-feat:checked+.toggle-label-feat{background-color:#9b9b9b}.toggle-switch-feat.light .toggle-input-feat:checked+.toggle-label-feat::before{transform:translateX(6px)}.toggle-switch-feat.dark .toggle-label-feat{background-color:#4b4b4b}.toggle-switch-feat.dark .toggle-input:checked+.toggle-label-feat{background-color:#717171}.toggle-switch-feat.dark .toggle-input-feat:checked+.toggle-label-feat::before{transform:translateX(16px)}.header-btns{width:100%;display:flex;justify-content:space-between}.shiny-output-error-validation{color:#000046;font-size:22px}
|
app/static/favicon.ico
ADDED
app/styles/main.scss
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
@import 'toggle_switches';
|
2 |
+
@import 'misc';
|
app/styles/media_query.scss
ADDED
File without changes
|
app/styles/misc.scss
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$dark-blue: #000046;
|
2 |
+
$light-blue: #1CB5E0;
|
3 |
+
$full-red: #cc1425;
|
4 |
+
$light-red: #dc3545;
|
5 |
+
|
6 |
+
.header-btns {
|
7 |
+
width: 100%;
|
8 |
+
display: flex;
|
9 |
+
justify-content: space-between;
|
10 |
+
|
11 |
+
}
|
12 |
+
|
13 |
+
|
14 |
+
.shiny-output-error-validation {
|
15 |
+
color: $dark-blue;
|
16 |
+
font-size: 22px;
|
17 |
+
}
|
app/styles/toggle_switches.scss
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.toggle-switch {
|
2 |
+
position: relative;
|
3 |
+
display: inline-block;
|
4 |
+
width: 40px;
|
5 |
+
height: 24px;
|
6 |
+
margin: 10px;
|
7 |
+
}
|
8 |
+
|
9 |
+
.toggle-switch .toggle-input {
|
10 |
+
display: none;
|
11 |
+
}
|
12 |
+
|
13 |
+
.toggle-switch .toggle-label {
|
14 |
+
position: absolute;
|
15 |
+
top: 0;
|
16 |
+
left: 0;
|
17 |
+
width: 40px;
|
18 |
+
height: 24px;
|
19 |
+
background-color: #ccc;
|
20 |
+
border-radius: 34px;
|
21 |
+
cursor: pointer;
|
22 |
+
transition: background-color 0.3s;
|
23 |
+
}
|
24 |
+
|
25 |
+
.toggle-switch .toggle-label::before {
|
26 |
+
content: "";
|
27 |
+
position: absolute;
|
28 |
+
width: 20px;
|
29 |
+
height: 20px;
|
30 |
+
border-radius: 50%;
|
31 |
+
top: 2px;
|
32 |
+
left: 2px;
|
33 |
+
background-color: #fff;
|
34 |
+
box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3);
|
35 |
+
transition: transform 0.3s;
|
36 |
+
}
|
37 |
+
|
38 |
+
.toggle-switch .toggle-input:checked + .toggle-label {
|
39 |
+
background-color: #2196F3;
|
40 |
+
}
|
41 |
+
|
42 |
+
.toggle-switch .toggle-input:checked + .toggle-label::before {
|
43 |
+
transform: translateX(16px);
|
44 |
+
}
|
45 |
+
|
46 |
+
.toggle-switch.light .toggle-label {
|
47 |
+
background-color: #BEBEBE;
|
48 |
+
}
|
49 |
+
|
50 |
+
.toggle-switch.light .toggle-input:checked + .toggle-label {
|
51 |
+
background-color: #9B9B9B;
|
52 |
+
}
|
53 |
+
|
54 |
+
.toggle-switch.light .toggle-input:checked + .toggle-label::before {
|
55 |
+
transform: translateX(6px);
|
56 |
+
}
|
57 |
+
|
58 |
+
.toggle-switch.dark .toggle-label {
|
59 |
+
background-color: #4B4B4B;
|
60 |
+
}
|
61 |
+
|
62 |
+
.toggle-switch.dark .toggle-input:checked + .toggle-label {
|
63 |
+
background-color: #717171;
|
64 |
+
}
|
65 |
+
|
66 |
+
.toggle-switch.dark .toggle-input:checked + .toggle-label::before {
|
67 |
+
transform: translateX(16px);
|
68 |
+
}
|
69 |
+
|
70 |
+
/* CHECKBOX FEATURE */
|
71 |
+
|
72 |
+
.feat-radio-container {
|
73 |
+
display: flex;
|
74 |
+
justify-content: center;
|
75 |
+
align-items: center;
|
76 |
+
}
|
77 |
+
|
78 |
+
.feat-radio-container > h4 {
|
79 |
+
margin-top: 6px;
|
80 |
+
}
|
81 |
+
|
82 |
+
.toggle-switch-feat {
|
83 |
+
position: relative;
|
84 |
+
display: inline-block;
|
85 |
+
width: 40px;
|
86 |
+
height: 24px;
|
87 |
+
margin: 10px;
|
88 |
+
}
|
89 |
+
|
90 |
+
.toggle-switch-feat .toggle-input-feat {
|
91 |
+
display: none;
|
92 |
+
}
|
93 |
+
|
94 |
+
.toggle-switch-feat .toggle-label-feat {
|
95 |
+
position: absolute;
|
96 |
+
top: 0;
|
97 |
+
left: 0;
|
98 |
+
width: 40px;
|
99 |
+
height: 24px;
|
100 |
+
background-color: #bbb;
|
101 |
+
border-radius: 34px;
|
102 |
+
cursor: pointer;
|
103 |
+
transition: background-color 0.3s;
|
104 |
+
}
|
105 |
+
|
106 |
+
.toggle-switch-feat .toggle-label-feat::before {
|
107 |
+
content: "";
|
108 |
+
position: absolute;
|
109 |
+
width: 20px;
|
110 |
+
height: 20px;
|
111 |
+
border-radius: 50%;
|
112 |
+
top: 2px;
|
113 |
+
left: 2px;
|
114 |
+
background-color: #fff;
|
115 |
+
box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.3);
|
116 |
+
transition: transform 0.3s;
|
117 |
+
}
|
118 |
+
|
119 |
+
.toggle-switch-feat .toggle-input-feat:checked + .toggle-label-feat {
|
120 |
+
background-color: #bbb;
|
121 |
+
}
|
122 |
+
|
123 |
+
.toggle-switch-feat .toggle-input-feat:checked + .toggle-label-feat::before {
|
124 |
+
transform: translateX(16px);
|
125 |
+
}
|
126 |
+
|
127 |
+
.toggle-switch-feat.light .toggle-label-feat {
|
128 |
+
background-color: #BEBEBE;
|
129 |
+
}
|
130 |
+
|
131 |
+
.toggle-switch-feat.light .toggle-input-feat:checked + .toggle-label-feat {
|
132 |
+
background-color: #9B9B9B;
|
133 |
+
}
|
134 |
+
|
135 |
+
.toggle-switch-feat.light .toggle-input-feat:checked + .toggle-label-feat::before {
|
136 |
+
transform: translateX(6px);
|
137 |
+
}
|
138 |
+
|
139 |
+
.toggle-switch-feat.dark .toggle-label-feat {
|
140 |
+
background-color: #4B4B4B;
|
141 |
+
}
|
142 |
+
|
143 |
+
.toggle-switch-feat.dark .toggle-input:checked + .toggle-label-feat {
|
144 |
+
background-color: #717171;
|
145 |
+
}
|
146 |
+
|
147 |
+
.toggle-switch-feat.dark .toggle-input-feat:checked + .toggle-label-feat::before {
|
148 |
+
transform: translateX(16px);
|
149 |
+
}
|
app/view/__init__.R
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
# View: Shiny modules and related code.
|
2 |
+
# https://go.appsilon.com/rhino-project-structure
|
app/view/browser.R
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
box::use(
|
2 |
+
shiny[moduleServer, NS, h1, fluidRow, column],
|
3 |
+
DT[renderDT, dataTableOutput],
|
4 |
+
htmlwidgets[JS]
|
5 |
+
)
|
6 |
+
|
7 |
+
#' @export
|
8 |
+
ui <- function(id) {
|
9 |
+
ns <- NS(id)
|
10 |
+
fluidRow(
|
11 |
+
column(
|
12 |
+
width = 12,
|
13 |
+
dataTableOutput(ns("browser_table"))
|
14 |
+
)
|
15 |
+
)
|
16 |
+
|
17 |
+
}
|
18 |
+
|
19 |
+
#' @export
|
20 |
+
server <- function(id, dataset) {
|
21 |
+
moduleServer(id, function(input, output, session) {
|
22 |
+
output$browser_table <- renderDT({
|
23 |
+
|
24 |
+
|
25 |
+
DT::datatable(
|
26 |
+
dataset,
|
27 |
+
options = list(
|
28 |
+
scrollX = TRUE,
|
29 |
+
scrollCollapse = TRUE,
|
30 |
+
initComplete = JS(
|
31 |
+
"function(settings, json) {",
|
32 |
+
"$(this.api().table().header()).css({'background-color': '#001f3f', 'color': 'white'});",
|
33 |
+
"}")
|
34 |
+
)
|
35 |
+
)
|
36 |
+
})
|
37 |
+
})
|
38 |
+
}
|
app/view/lollipop.R
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
box::use(
|
2 |
+
shiny[...],
|
3 |
+
bs4Dash[box],
|
4 |
+
g3viz[...]
|
5 |
+
)
|
6 |
+
|
7 |
+
#' @export
|
8 |
+
ui <- function(id) {
|
9 |
+
ns <- NS(id)
|
10 |
+
tagList(
|
11 |
+
|
12 |
+
box(
|
13 |
+
width = 12,
|
14 |
+
status = "navy",
|
15 |
+
solidHeader = TRUE,
|
16 |
+
align = "center",
|
17 |
+
title = "Lollipop plot",
|
18 |
+
id = "lollipop_plot_box",
|
19 |
+
g3LollipopOutput(ns("lollipop_plot"), height = "700px")
|
20 |
+
)
|
21 |
+
)
|
22 |
+
}
|
23 |
+
|
24 |
+
#' @export
|
25 |
+
server <- function(id, data, selected_gene_rval) {
|
26 |
+
moduleServer(id, function(input, output, session) {
|
27 |
+
|
28 |
+
output$lollipop_plot <- renderG3Lollipop({
|
29 |
+
req(selected_gene_rval())
|
30 |
+
print(selected_gene_rval())
|
31 |
+
filter_tab <- data[gene_name == selected_gene_rval()]
|
32 |
+
plot.options <- g3Lollipop.theme(theme.name = "cbioportal",
|
33 |
+
title.text = selected_gene_rval(),
|
34 |
+
y.axis.label = "# of Mutations")
|
35 |
+
|
36 |
+
g3viz::g3Lollipop(
|
37 |
+
mutation.dat = filter_tab,
|
38 |
+
gene.symbol = selected_gene_rval(),
|
39 |
+
gene.symbol.col = "gene_name",
|
40 |
+
aa.pos.col = "Protein_position",
|
41 |
+
protein.change.col = "HGVSp",
|
42 |
+
factor.col = "Consequence",
|
43 |
+
btn.style = "gray", # gray-style chart download buttons
|
44 |
+
plot.options = plot.options,
|
45 |
+
)
|
46 |
+
})
|
47 |
+
})
|
48 |
+
}
|
app/view/sankey.R
ADDED
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
box::use(
|
2 |
+
shiny[moduleServer, verbatimTextOutput, renderPrint, isolate, NS, req, validate, need, tagList, renderPlot, plotOutput, observeEvent, reactiveVal, reactiveValues],
|
3 |
+
plotly[plot_ly, event_data, plotlyOutput, renderPlotly, plotlyProxy, plotlyProxyInvoke],
|
4 |
+
data.table[...],
|
5 |
+
bs4Dash[box],
|
6 |
+
stats[na.omit],
|
7 |
+
shinyjs[...],
|
8 |
+
gargoyle[on]
|
9 |
+
)
|
10 |
+
|
11 |
+
box::use(
|
12 |
+
app/logic/create_sankey[sankey_prepare_data, create_sankey]
|
13 |
+
)
|
14 |
+
|
15 |
+
#' @export
|
16 |
+
ui <- function(id) {
|
17 |
+
ns <- NS(id)
|
18 |
+
tagList(
|
19 |
+
|
20 |
+
box(
|
21 |
+
width = 12,
|
22 |
+
height = "50vh",
|
23 |
+
title = "Sankey Diagram",
|
24 |
+
status = "navy",
|
25 |
+
maximizable = TRUE,
|
26 |
+
solidHeader = TRUE,
|
27 |
+
id = ns("sankey_box"),
|
28 |
+
plotlyOutput(ns("sankey_plt"), height = "50vh")
|
29 |
+
|
30 |
+
)
|
31 |
+
)
|
32 |
+
}
|
33 |
+
|
34 |
+
#' @export
|
35 |
+
server <- function(id, dataset, selected_pathways_react, selected_genes_react, is_path_tab_active) {
|
36 |
+
moduleServer(id, function(input, output, session) {
|
37 |
+
ns <- NS(id)
|
38 |
+
# reactive value holding the sankey plot
|
39 |
+
|
40 |
+
|
41 |
+
# reactiveVal initialization ----------------------------------------------
|
42 |
+
|
43 |
+
sankey_plot <- reactiveVal()
|
44 |
+
sankey_pathway_plot <- reactiveVal()
|
45 |
+
sankey_gene_plot <- reactiveVal()
|
46 |
+
labels <- reactiveVal()
|
47 |
+
path_gene_tab <- reactiveVal()
|
48 |
+
gene_variant_tab <- reactiveVal()
|
49 |
+
selected_gene_name <- reactiveVal()
|
50 |
+
|
51 |
+
|
52 |
+
# Render ------------------------------------------------------------------
|
53 |
+
|
54 |
+
output$sankey_plt <- renderPlotly({
|
55 |
+
validate(
|
56 |
+
need(!((is.null(selected_pathways_react()) || nrow(selected_pathways_react()) == 0)
|
57 |
+
&& (is.null(selected_genes_react()) || nrow(selected_genes_react()) == 0)),
|
58 |
+
"select the features (pathways / genes) that you want to visualize from the table on the right")
|
59 |
+
)
|
60 |
+
# output the contents of the sankey_plot reactive value
|
61 |
+
if (is_path_tab_active()) {
|
62 |
+
sankey_pathway_plot()
|
63 |
+
} else {
|
64 |
+
sankey_gene_plot()
|
65 |
+
}
|
66 |
+
})
|
67 |
+
|
68 |
+
|
69 |
+
|
70 |
+
# Events ------------------------------------------------------------------
|
71 |
+
|
72 |
+
|
73 |
+
on("reset_sankey", {
|
74 |
+
if (is_path_tab_active()) {
|
75 |
+
sankey_pathway_plot(NULL)
|
76 |
+
} else {
|
77 |
+
sankey_gene_plot(NULL)
|
78 |
+
}
|
79 |
+
})
|
80 |
+
|
81 |
+
# data is passed into module -> create sankey
|
82 |
+
observeEvent(selected_pathways_react(), {
|
83 |
+
req(selected_pathways_react())
|
84 |
+
|
85 |
+
data <- sankey_prepare_data(dataset, selected_pathways_react(), selected_genes_react(), TRUE)
|
86 |
+
labels(data[[1]])
|
87 |
+
path_gene <- unique(data[[2]][, .(kegg_paths_name, gene_name, first, second)])
|
88 |
+
gene_variant <- unique(data[[2]][, .(gene_name, var_name, second, third)])
|
89 |
+
setorder(path_gene, first)
|
90 |
+
setorder(gene_variant, second)
|
91 |
+
path_gene_tab(path_gene)
|
92 |
+
gene_variant_tab(gene_variant)
|
93 |
+
|
94 |
+
plot <- create_sankey(labels(), path_gene, gene_variant, data[[3]])
|
95 |
+
sankey_pathway_plot(plot)
|
96 |
+
})
|
97 |
+
|
98 |
+
observeEvent(selected_genes_react(), {
|
99 |
+
req(selected_genes_react())
|
100 |
+
print(selected_genes_react())
|
101 |
+
data <- sankey_prepare_data(dataset, selected_pathways_react(), selected_genes_react(), FALSE)
|
102 |
+
labels(data[[1]])
|
103 |
+
path_gene <- unique(data[[2]][, .(kegg_paths_name, gene_name, first, second)])
|
104 |
+
gene_variant <- unique(data[[2]][, .(gene_name, var_name, second, third)])
|
105 |
+
setorder(path_gene, first)
|
106 |
+
setorder(gene_variant, second)
|
107 |
+
path_gene_tab(path_gene)
|
108 |
+
gene_variant_tab(gene_variant)
|
109 |
+
plot <- create_sankey(labels(), path_gene, gene_variant, data[[3]])
|
110 |
+
sankey_gene_plot(plot)
|
111 |
+
})
|
112 |
+
|
113 |
+
|
114 |
+
observeEvent(event_data("plotly_click", priority = "event"), {
|
115 |
+
click_data <- event_data("plotly_click", priority = "event")
|
116 |
+
req(click_data)
|
117 |
+
|
118 |
+
result <- sankey_link_selection(click_data, path_gene_tab(), gene_variant_tab())
|
119 |
+
link_ids <- result[[1]]
|
120 |
+
gene_name <- result[[2]]
|
121 |
+
selected_gene_name(gene_name)
|
122 |
+
|
123 |
+
link_colors <- rep("lightgray", nrow(labels()))
|
124 |
+
link_colors[link_ids] <- "red"
|
125 |
+
|
126 |
+
proxy <- plotlyProxy("sankey_plt", session)
|
127 |
+
plotlyProxyInvoke(proxy, "restyle",
|
128 |
+
list(link.color = list(link_colors)))
|
129 |
+
|
130 |
+
})
|
131 |
+
|
132 |
+
|
133 |
+
observeEvent(input$sankey_box$maximized, {
|
134 |
+
plot_height <- if (input$sankey_box$maximized) {
|
135 |
+
"100%"
|
136 |
+
} else {
|
137 |
+
"50vh"
|
138 |
+
}
|
139 |
+
|
140 |
+
js_call <- sprintf(
|
141 |
+
"
|
142 |
+
setTimeout(() => {
|
143 |
+
$('#%s').css('height', '%s');
|
144 |
+
}, 500)
|
145 |
+
$('#%s').trigger('resize');
|
146 |
+
",
|
147 |
+
"app-sankey-sankey_plt",
|
148 |
+
plot_height,
|
149 |
+
"app-sankey-sankey_plt"
|
150 |
+
)
|
151 |
+
shinyjs::runjs(js_call)
|
152 |
+
}, ignoreInit = TRUE)
|
153 |
+
|
154 |
+
return(selected_gene_name)
|
155 |
+
})
|
156 |
+
}
|
157 |
+
|
158 |
+
|
159 |
+
#' sankey_link_selection
|
160 |
+
#'
|
161 |
+
#' @param click_data
|
162 |
+
#' @param path_gene_tab
|
163 |
+
#' @param gene_variant_tab
|
164 |
+
#'
|
165 |
+
#' @return
|
166 |
+
#' @export
|
167 |
+
#'
|
168 |
+
#' @examples
|
169 |
+
sankey_link_selection <- function(click_data, path_gene_tab, gene_variant_tab) {
|
170 |
+
clicked_link <- click_data$pointNumber + 1
|
171 |
+
|
172 |
+
if (clicked_link <= nrow(path_gene_tab)) {
|
173 |
+
return(first_level_link_selection(clicked_link, path_gene_tab, gene_variant_tab))
|
174 |
+
}
|
175 |
+
return(second_level_link_selection(clicked_link, path_gene_tab, gene_variant_tab))
|
176 |
+
}
|
177 |
+
|
178 |
+
|
179 |
+
#' first_level_link_selection
|
180 |
+
#'
|
181 |
+
#' @param clicked_link
|
182 |
+
#' @param path_gene_tab
|
183 |
+
#' @param gene_variant_tab
|
184 |
+
#'
|
185 |
+
#' @return
|
186 |
+
#' @export
|
187 |
+
#'
|
188 |
+
#' @examples
|
189 |
+
first_level_link_selection <- function(clicked_link, path_gene_tab, gene_variant_tab) {
|
190 |
+
select_gene_name <- path_gene_tab[clicked_link]$gene_name
|
191 |
+
labels <- data.table(label = c(path_gene_tab$kegg_paths_name, gene_variant_tab$gene_name), id = c(path_gene_tab$first, gene_variant_tab$second))
|
192 |
+
first_links <- clicked_link
|
193 |
+
|
194 |
+
second_links <- which(labels$label == select_gene_name)
|
195 |
+
link_ids <- unique(c(
|
196 |
+
first_links,
|
197 |
+
second_links)
|
198 |
+
)
|
199 |
+
return(list(link_ids, select_gene_name))
|
200 |
+
}
|
201 |
+
|
202 |
+
|
203 |
+
#' second_level_link_selection
|
204 |
+
#'
|
205 |
+
#' @param clicked_link
|
206 |
+
#' @param path_gene_tab
|
207 |
+
#' @param gene_variant_tab
|
208 |
+
#'
|
209 |
+
#' @return
|
210 |
+
#' @export
|
211 |
+
#'
|
212 |
+
#' @examples
|
213 |
+
second_level_link_selection <- function(clicked_link, path_gene_tab, gene_variant_tab) {
|
214 |
+
select_gene_name <- gene_variant_tab[(clicked_link - nrow(path_gene_tab))]$gene_name
|
215 |
+
labels <- data.table(label = c(path_gene_tab$kegg_paths_name, gene_variant_tab$gene_name), id = c(path_gene_tab$first, gene_variant_tab$second))
|
216 |
+
|
217 |
+
first_links <- which(path_gene_tab$gene_name == select_gene_name)
|
218 |
+
second_links <- which(labels$label == select_gene_name)
|
219 |
+
|
220 |
+
link_ids <- unique(c(
|
221 |
+
first_links,
|
222 |
+
second_links)
|
223 |
+
)
|
224 |
+
return(list(link_ids, select_gene_name))
|
225 |
+
}
|
226 |
+
|
227 |
+
|
app/view/table.R
ADDED
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
box::use(
|
2 |
+
shiny[moduleServer, req, NS, observeEvent, tagList, reactiveVal, h3, div, reactive, HTML],
|
3 |
+
bs4Dash[actionButton, useAutoColor, box],
|
4 |
+
DT[renderDataTable, dataTableOutput],
|
5 |
+
gargoyle[trigger]
|
6 |
+
)
|
7 |
+
|
8 |
+
box::use(
|
9 |
+
app/logic/render_table[render_pathways_table, render_genes_table]
|
10 |
+
)
|
11 |
+
|
12 |
+
|
13 |
+
|
14 |
+
#' @export
|
15 |
+
ui <- function(id) {
|
16 |
+
ns <- NS(id)
|
17 |
+
tagList(
|
18 |
+
box(
|
19 |
+
solidHeader = TRUE,
|
20 |
+
title = div(
|
21 |
+
class = "header-btns",
|
22 |
+
bs4Dash::actionButton(
|
23 |
+
ns("reset_btn"),
|
24 |
+
label = "Reset selection",
|
25 |
+
status = "secondary"
|
26 |
+
),
|
27 |
+
HTML(
|
28 |
+
'
|
29 |
+
<div id="feat-radio" style="margin-left: 15px" class="feat-radio-container">
|
30 |
+
<h4>Pathway</h4>
|
31 |
+
<div class="toggle-switch-feat">
|
32 |
+
<input name="checkbox-feature" class="toggle-input-feat" id="toggle-feat" type="checkbox">
|
33 |
+
<label class="toggle-label-feat" for="toggle-feat"></label>
|
34 |
+
</div>
|
35 |
+
<h4>Gene</h4>
|
36 |
+
</div>
|
37 |
+
|
38 |
+
<script>
|
39 |
+
|
40 |
+
var checkbox = document.querySelector("input[name=checkbox-feature]");
|
41 |
+
checkbox.addEventListener("change", function() {
|
42 |
+
if (this.checked) {
|
43 |
+
Shiny.setInputValue("app-table-feature_radio", "gene", {priority: "event"});
|
44 |
+
} else {
|
45 |
+
Shiny.setInputValue("app-table-feature_radio", "pathway", {priority: "event"});
|
46 |
+
}
|
47 |
+
});
|
48 |
+
|
49 |
+
</script>
|
50 |
+
|
51 |
+
'
|
52 |
+
)
|
53 |
+
),
|
54 |
+
collapsible = TRUE,
|
55 |
+
status = "navy",
|
56 |
+
width = 12,
|
57 |
+
dataTableOutput(ns("table"), height = "40%")
|
58 |
+
)
|
59 |
+
)
|
60 |
+
|
61 |
+
}
|
62 |
+
|
63 |
+
#' @export
|
64 |
+
server <- function(id, data) {
|
65 |
+
|
66 |
+
moduleServer(id, function(input, output, session) {
|
67 |
+
is_path_tab_active <- reactiveVal(TRUE)
|
68 |
+
selected_pathways <- reactiveVal(NULL)
|
69 |
+
selected_genes <- reactiveVal(NULL)
|
70 |
+
|
71 |
+
pathways_tab <- reactive({
|
72 |
+
tab <- unique(data[, .(kegg_paths_name)])
|
73 |
+
colnames(tab) <- "pathway"
|
74 |
+
return(tab)
|
75 |
+
})
|
76 |
+
|
77 |
+
genes_tab <- reactive({
|
78 |
+
unique(data[, .(gene_name)])
|
79 |
+
})
|
80 |
+
|
81 |
+
observeEvent(pathways_tab(),{
|
82 |
+
req(pathways_tab())
|
83 |
+
output$table <- render_pathways_table(
|
84 |
+
pathways_tab(),
|
85 |
+
selected_pathways()
|
86 |
+
)
|
87 |
+
})
|
88 |
+
|
89 |
+
|
90 |
+
observeEvent(input$feature_radio, {
|
91 |
+
if (input$feature_radio == "gene") {
|
92 |
+
if (is_path_tab_active()) {
|
93 |
+
is_path_tab_active(FALSE)
|
94 |
+
output$table <- render_genes_table(
|
95 |
+
genes_tab(),
|
96 |
+
selected_genes()$row_i
|
97 |
+
)
|
98 |
+
}
|
99 |
+
|
100 |
+
} else if (input$feature_radio == "pathway") {
|
101 |
+
if (!(is_path_tab_active())) {
|
102 |
+
is_path_tab_active(TRUE)
|
103 |
+
output$table <- render_pathways_table(
|
104 |
+
pathways_tab(),
|
105 |
+
selected_pathways()$row_i
|
106 |
+
)
|
107 |
+
|
108 |
+
}
|
109 |
+
}
|
110 |
+
|
111 |
+
})
|
112 |
+
|
113 |
+
|
114 |
+
observeEvent(input$reset_btn, {
|
115 |
+
if ((is_path_tab_active())) {
|
116 |
+
selected_pathways(NULL)
|
117 |
+
output$table <- render_pathways_table(
|
118 |
+
pathways_tab()
|
119 |
+
)
|
120 |
+
trigger("reset_sankey")
|
121 |
+
|
122 |
+
}
|
123 |
+
else {
|
124 |
+
selected_genes(NULL)
|
125 |
+
output$table <- render_genes_table(
|
126 |
+
genes_tab()
|
127 |
+
)
|
128 |
+
}
|
129 |
+
|
130 |
+
|
131 |
+
})
|
132 |
+
|
133 |
+
|
134 |
+
############# FEATURE SELECTION ##########
|
135 |
+
observeEvent(input$table_cell_clicked, {
|
136 |
+
gene <- input$table_cell_clicked[["value"]]
|
137 |
+
row <- input$table_cell_clicked[["row"]]
|
138 |
+
req(gene)
|
139 |
+
if (is.null(row)) {
|
140 |
+
print("it is null")
|
141 |
+
return(NULL)
|
142 |
+
}
|
143 |
+
|
144 |
+
rows <- input$table_rows_selected
|
145 |
+
if (is_path_tab_active()) {
|
146 |
+
features <- pathways_tab()[rows]
|
147 |
+
features[, row_i := rows]
|
148 |
+
selected_pathways(features)
|
149 |
+
} else {
|
150 |
+
features <- genes_tab()[rows]
|
151 |
+
features[, row_i := rows]
|
152 |
+
selected_genes(features)
|
153 |
+
}
|
154 |
+
print(selected_pathways())
|
155 |
+
})
|
156 |
+
|
157 |
+
return(reactiveVal(list(selected_pathways, selected_genes, is_path_tab_active)))
|
158 |
+
})
|
159 |
+
}
|
160 |
+
|
161 |
+
|
162 |
+
|