vojtam commited on
Commit
c038271
1 Parent(s): 99d1d30

upload app directory

Browse files
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
+