From 0b0ac934f2af0878583b8cb66e12e24e89bebfb6 Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Thu, 5 Mar 2026 11:13:57 -0500 Subject: [PATCH 1/6] add export HTML button in frontend --- R/module-visualize-network-server.R | 6 ++++++ R/module-visualize-network-ui.R | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/R/module-visualize-network-server.R b/R/module-visualize-network-server.R index 60a8097..b486956 100644 --- a/R/module-visualize-network-server.R +++ b/R/module-visualize-network-server.R @@ -586,6 +586,12 @@ visualizeNetworkServer <- function(id, parent_session, dataComparison) { downloadButton(ns("network_download_code"), "Download analysis code", icon("download"), style="color: #000000; background-color: #75ba82; border-color: #000000") }) + + output$network.html.button <- renderUI({ + ns <- session$ns + downloadButton(ns("network_html_code"), "Download HTML", icon("download"), + style="color: #000000; background-color: #75ba82; border-color: #000000") + }) }) output$network_download_code <- downloadHandler( diff --git a/R/module-visualize-network-ui.R b/R/module-visualize-network-ui.R index 9c40afd..ab09496 100644 --- a/R/module-visualize-network-ui.R +++ b/R/module-visualize-network-ui.R @@ -395,8 +395,10 @@ createDashboardBody <- function(ns) { createCodeDownloadBox <- function(ns) { div( - style = "text-align: right; padding: 10px;", + style = "text-align: right; padding: 10px; display: flex; justify-content: flex-end; gap: 10px;", + uiOutput(ns("network.html.button")), uiOutput(ns("network.code.button")) + ) } From c66009e3a75815c647b4149d6389ee4934374a10 Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Thu, 5 Mar 2026 11:25:01 -0500 Subject: [PATCH 2/6] finalize export HTML feature --- R/module-visualize-network-server.R | 35 +++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/R/module-visualize-network-server.R b/R/module-visualize-network-server.R index b486956..6ac22a9 100644 --- a/R/module-visualize-network-server.R +++ b/R/module-visualize-network-server.R @@ -541,8 +541,7 @@ visualizeNetworkServer <- function(id, parent_session, dataComparison) { codes <- paste(codes, "# Visualize network on web browser and export as an HTML file\n", sep = "") displayLabelTypeStr <- paste0("\"", paste(input$displayLabelType, collapse = "\", \""), "\"") codes <- paste(codes, "cytoscapeNetwork(subnetwork$nodes, subnetwork$edges, displayLabelType=", displayLabelTypeStr, ")\n", sep = "") - codes <- paste(codes, "widget = cytoscapeNetwork(subnetwork$nodes, subnetwork$edges, displayLabelType=", displayLabelTypeStr, ")\n", sep = "") - codes <- paste(codes, "htmlwidgets::saveWidget(widget,\n file = \"network.html\",\n selfcontained = TRUE\n)") + codes <- paste(codes, "exportNetworkToHTML(subnetwork$nodes, subnetwork$edges, displayLabelType=", displayLabelTypeStr, ")\n", sep = "") return(codes) }) @@ -614,6 +613,38 @@ visualizeNetworkServer <- function(id, parent_session, dataComparison) { } ) + output$network_html_code <- downloadHandler( + filename = function() { + paste("network-", Sys.Date(), ".html", sep = "") + }, + content = function(file) { + tryCatch({ + render_data <- networkVisualization() + if (is.null(render_data)) { + stop("No network to export. Please ensure network is displayed first.") + } + + tmp_file <- file.path(tempdir(), paste0("network-", Sys.Date(), ".html")) + + exportNetworkToHTML( + nodes = render_data$nodes_table, + edges = render_data$edges_table, + nodeFontSize = 12, + displayLabelType = input$displayLabelType, + filename = tmp_file + ) + + file.copy(tmp_file, file) + + }, error = function(e) { + showNotification( + paste("Error downloading HTML:", e$message), + type = "error" + ) + }) + } + ) + # Observe edge click events observeEvent(input$network_edge_clicked, { edge_data <- input$network_edge_clicked From 270031f04e2be74ccf924bd597cdcc3f32bf00f4 Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Thu, 5 Mar 2026 11:26:07 -0500 Subject: [PATCH 3/6] create download boxes finalized --- R/module-visualize-network-ui.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/module-visualize-network-ui.R b/R/module-visualize-network-ui.R index ab09496..d70cc46 100644 --- a/R/module-visualize-network-ui.R +++ b/R/module-visualize-network-ui.R @@ -357,7 +357,7 @@ createNetworkSettingsTab <- function(ns) { style = "margin: 0;" ) ), - createCodeDownloadBox(ns), + createDownloadBoxes(ns), createNetworkVisualizationBox(ns), createEdgesTableBox(ns), createNodesTableBox(ns) @@ -393,7 +393,7 @@ createDashboardBody <- function(ns) { ) } -createCodeDownloadBox <- function(ns) { +createDownloadBoxes <- function(ns) { div( style = "text-align: right; padding: 10px; display: flex; justify-content: flex-end; gap: 10px;", uiOutput(ns("network.html.button")), From a909031f833424969e6e4780e62ebb53cb81e102 Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Thu, 5 Mar 2026 11:35:10 -0500 Subject: [PATCH 4/6] add unit tests --- R/module-visualize-network-server.R | 40 ++++++++++----------- tests/testthat/test_network_visualization.R | 26 ++++++++++++++ 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/R/module-visualize-network-server.R b/R/module-visualize-network-server.R index 6ac22a9..f446dfe 100644 --- a/R/module-visualize-network-server.R +++ b/R/module-visualize-network-server.R @@ -226,6 +226,24 @@ extractSubnetwork <- function(annotated_df, pValue, evidence, statementTypes, }) } +export_network_html <- function(render_data, displayLabelType, file) { + if (is.null(render_data)) { + stop("No network to export. Please ensure network is displayed first.") + } + + tmp_file <- file.path(tempdir(), paste0("network-", Sys.Date(), ".html")) + + exportNetworkToHTML( + nodes = render_data$nodes_table, + edges = render_data$edges_table, + nodeFontSize = 12, + displayLabelType = displayLabelType, + filename = tmp_file + ) + + file.copy(tmp_file, file) +} + # ============================================================================= # MAIN SERVER FUNCTION - Updated to use decoupled architecture # ============================================================================= @@ -620,27 +638,9 @@ visualizeNetworkServer <- function(id, parent_session, dataComparison) { content = function(file) { tryCatch({ render_data <- networkVisualization() - if (is.null(render_data)) { - stop("No network to export. Please ensure network is displayed first.") - } - - tmp_file <- file.path(tempdir(), paste0("network-", Sys.Date(), ".html")) - - exportNetworkToHTML( - nodes = render_data$nodes_table, - edges = render_data$edges_table, - nodeFontSize = 12, - displayLabelType = input$displayLabelType, - filename = tmp_file - ) - - file.copy(tmp_file, file) - + export_network_html(render_data, input$displayLabelType, file) }, error = function(e) { - showNotification( - paste("Error downloading HTML:", e$message), - type = "error" - ) + showNotification(paste("Error downloading HTML:", e$message), type = "error") }) } ) diff --git a/tests/testthat/test_network_visualization.R b/tests/testthat/test_network_visualization.R index 7a1b789..4b28b90 100644 --- a/tests/testthat/test_network_visualization.R +++ b/tests/testthat/test_network_visualization.R @@ -289,4 +289,30 @@ test_that("updateProteinChoices updates selectizeInput correctly", { # Verify the mock was called expect_called(mock_update, 1) +}) + +test_that("export_network_html writes HTML file correctly", { + mock_render_data <- list( + nodes_table = data.frame(id = c("A", "B")), + edges_table = data.frame(source = "A", target = "B") + ) + + tmp_output <- tempfile(fileext = ".html") + + stub(export_network_html, "exportNetworkToHTML", function(..., filename) { + writeLines("mock network", filename) + }) + + export_network_html(mock_render_data, "hgncName", tmp_output) + + expect_true(file.exists(tmp_output)) + expect_gt(file.size(tmp_output), 0) + expect_true(any(grepl("", readLines(tmp_output)))) +}) + +test_that("export_network_html errors when render_data is NULL", { + expect_error( + export_network_html(NULL, "hgncName", tempfile()), + "No network to export. Please ensure network is displayed first." + ) }) \ No newline at end of file From 0dd5c93d91631905477f491cf4d90e5de88f5f83 Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Thu, 5 Mar 2026 11:41:47 -0500 Subject: [PATCH 5/6] address coderabbit comments --- NAMESPACE | 1 + R/MSstatsShiny.R | 1 + R/module-visualize-network-server.R | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NAMESPACE b/NAMESPACE index ed41e9d..b214184 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -38,6 +38,7 @@ importFrom(DT,renderDT) importFrom(DT,renderDataTable) importFrom(Hmisc,describe) importFrom(MSstatsBioNet,annotateProteinInfoFromIndra) +importFrom(MSstatsBioNet,exportNetworkToHTML) importFrom(MSstatsBioNet,getSubnetworkFromIndra) importFrom(MSstatsConvert,MSstatsLogsSettings) importFrom(MSstatsPTM,FragPipetoMSstatsPTMFormat) diff --git a/R/MSstatsShiny.R b/R/MSstatsShiny.R index 4287070..ce2215b 100644 --- a/R/MSstatsShiny.R +++ b/R/MSstatsShiny.R @@ -36,6 +36,7 @@ #' @importFrom tidyr unite pivot_wider #' @importFrom MSstatsConvert MSstatsLogsSettings #' @importFrom MSstatsPTM dataProcessPlotsPTM groupComparisonPlotsPTM MaxQtoMSstatsPTMFormat PDtoMSstatsPTMFormat FragPipetoMSstatsPTMFormat SkylinetoMSstatsPTMFormat MetamorpheusToMSstatsPTMFormat SpectronauttoMSstatsPTMFormat +#' @importFrom MSstatsBioNet exportNetworkToHTML #' @importFrom utils capture.output head packageVersion read.csv read.delim write.csv #' @importFrom stats aggregate #' @importFrom methods is diff --git a/R/module-visualize-network-server.R b/R/module-visualize-network-server.R index f446dfe..51d820b 100644 --- a/R/module-visualize-network-server.R +++ b/R/module-visualize-network-server.R @@ -241,7 +241,10 @@ export_network_html <- function(render_data, displayLabelType, file) { filename = tmp_file ) - file.copy(tmp_file, file) + copied <- file.copy(tmp_file, file, overwrite = TRUE) + if (!copied) { + stop("Failed to prepare HTML download file.") + } } # ============================================================================= From 828f714b05b50711589c39f176756164d09fe6e9 Mon Sep 17 00:00:00 2001 From: Tony Wu Date: Thu, 5 Mar 2026 11:53:11 -0500 Subject: [PATCH 6/6] trigger build --- tests/testthat/test_network_visualization.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test_network_visualization.R b/tests/testthat/test_network_visualization.R index 4b28b90..d47664c 100644 --- a/tests/testthat/test_network_visualization.R +++ b/tests/testthat/test_network_visualization.R @@ -312,7 +312,7 @@ test_that("export_network_html writes HTML file correctly", { test_that("export_network_html errors when render_data is NULL", { expect_error( - export_network_html(NULL, "hgncName", tempfile()), + export_network_html(NULL, "hgncName", tempfile()), "No network to export. Please ensure network is displayed first." ) }) \ No newline at end of file