Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Depends: R (>= 4.2)
Imports: shiny, shinyBS, shinyjs, shinybusy, dplyr, ggplot2, plotly, data.table, Hmisc,
MSstats, MSstatsTMT, MSstatsPTM, MSstatsConvert, gplots, marray, DT, readxl,
ggrepel, uuid, utils, stats, htmltools, methods, tidyr, grDevices, graphics, mockery, MSstatsBioNet,
shinydashboard, arrow, tools
shinydashboard, arrow, tools, MSstatsResponse
Suggests:
rmarkdown,
tinytest,
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ importFrom(MSstatsPTM,SkylinetoMSstatsPTMFormat)
importFrom(MSstatsPTM,SpectronauttoMSstatsPTMFormat)
importFrom(MSstatsPTM,dataProcessPlotsPTM)
importFrom(MSstatsPTM,groupComparisonPlotsPTM)
importFrom(MSstatsResponse,convertGroupToNumericDose)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
importFrom(arrow,read_parquet)
importFrom(data.table,copy)
importFrom(dplyr,`%>%`)
Expand Down
3 changes: 2 additions & 1 deletion R/constants.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ CONSTANTS_STATMODEL = list(
comparison_mode_all_pairwise = "comparison_mode_all_pairwise",
comparison_mode_all_vs_one = "comparison_mode_all_vs_one",
comparison_mode_custom_pairwise = "comparison_mode_custom_pairwise",
comparison_mode_custom_nonpairwise = "comparison_mode_custom_nonpairwise"
comparison_mode_custom_nonpairwise = "comparison_mode_custom_nonpairwise",
comparison_mode_response_curve = "comparison_mode_response_curve"
)
12 changes: 12 additions & 0 deletions R/module-statmodel-server.R
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ get_contrast_panel_ui <- function(mode, ns) {
build_all_pairwise_panel(ns)
} else if (mode == CONSTANTS_STATMODEL$comparison_mode_custom_nonpairwise) {
build_custom_nonpairwise_panel(ns)
} else if (mode == CONSTANTS_STATMODEL$comparison_mode_response_curve) {
build_response_curve_panel(ns)
} else {
NULL
}
Expand Down Expand Up @@ -202,6 +204,13 @@ build_all_pair_contrast = function(input, condition_list, contrast, comp_list, r
return(contrast$matrix)
}

#' @importFrom MSstatsResponse convertGroupToNumericDose
build_response_curve_matrix = function(contrast, condition_list) {
condition_to_metadata_table = convertGroupToNumericDose(condition_list)
contrast$matrix = data.frame(GROUP = condition_list, condition_to_metadata_table)
return(contrast$matrix)
}
Comment on lines +207 to +212
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error handling for convertGroupToNumericDose.

The call to convertGroupToNumericDose can fail if condition names don't follow expected patterns (e.g., no numeric dose extractable). Other builder functions validate inputs or handle edge cases gracefully.

Consider wrapping in a tryCatch to provide a user-friendly error message:

 #' @importFrom MSstatsResponse convertGroupToNumericDose
 build_response_curve_matrix = function(contrast, condition_list) {
-  condition_to_metadata_table = convertGroupToNumericDose(condition_list)
-  contrast$matrix = data.frame(GROUP = condition_list, condition_to_metadata_table)
+  tryCatch({
+    condition_to_metadata_table = convertGroupToNumericDose(condition_list)
+    contrast$matrix = data.frame(GROUP = condition_list, condition_to_metadata_table)
+  }, error = function(e) {
+    stop("Failed to extract dose information from condition names. ",
+         "Ensure conditions follow a pattern like 'Drug_100nM'. Error: ", 
+         conditionMessage(e))
+  })
   return(contrast$matrix)
 }
🤖 Prompt for AI Agents
In R/module-statmodel-server.R around lines 207 to 212, the call to
convertGroupToNumericDose can throw an error for unexpected condition names;
wrap the call in a tryCatch that captures errors and stops with a clear,
user-friendly message (e.g., mentioning which input condition_list failed and
suggesting valid formats), and ensure that on error you do not assign an invalid
value to contrast$matrix; on success proceed to set contrast$matrix as before
and return it.


# Todo: Add helper function to build dose response curve mapper matrix

# ============================================================================
Expand Down Expand Up @@ -509,6 +518,9 @@ statmodelServer = function(id, parent_session, loadpage_input, qc_input,
} else if (input[[NAMESPACE_STATMODEL$comparison_mode]] == CONSTANTS_STATMODEL$comparison_mode_all_pairwise) {
contrast$matrix = build_all_pair_contrast(
input, condition_list(), contrast, comp_list, row(), loadpage_input())
} else if (input[[NAMESPACE_STATMODEL$comparison_mode]] == CONSTANTS_STATMODEL$comparison_mode_response_curve) {
contrast$matrix = build_response_curve_matrix(
contrast, condition_list())
Comment on lines +521 to +523
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for all usages of comparison_mode_response_curve
rg -n "comparison_mode_response_curve" R

Repository: Vitek-Lab/MSstatsShiny

Length of output: 645


🏁 Script executed:

#!/bin/bash
# Get context around line 35
echo "=== Context at line 35 ===" 
sed -n '25,50p' R/module-statmodel-server.R

echo ""
echo "=== Context at line 100 ==="
sed -n '90,115p' R/module-statmodel-server.R

echo ""
echo "=== Context at line 542 (reviewed code) ==="
sed -n '535,555p' R/module-statmodel-server.R

Repository: Vitek-Lab/MSstatsShiny

Length of output: 3519


🏁 Script executed:

#!/bin/bash
# Find dataComparison function
echo "=== Searching for dataComparison function ==="
rg -n "dataComparison\s*=" R --type r

echo ""
echo "=== Searching for generate_analysis_code function ==="
rg -n "generate_analysis_code\s*=" R --type r

echo ""
echo "=== Searching for build_response_curve_matrix function ==="
rg -n "build_response_curve_matrix\s*=" R --type r

echo ""
echo "=== Searching for uses of contrast\$matrix ==="
rg -n "contrast\\\$matrix" R --type r | head -30

Repository: Vitek-Lab/MSstatsShiny

Length of output: 2847


🏁 Script executed:

#!/bin/bash
# View build_response_curve_matrix function
echo "=== build_response_curve_matrix function ==="
sed -n '229,280p' R/module-statmodel-server.R

echo ""
echo "=== generate_analysis_code function ==="
sed -n '335,380p' R/module-statmodel-server.R

Repository: Vitek-Lab/MSstatsShiny

Length of output: 4438


🏁 Script executed:

#!/bin/bash
# Find where generate_analysis_code is called
echo "=== Calls to generate_analysis_code ==="
rg -n "generate_analysis_code" R --type r -B 2 -A 2

echo ""
echo "=== data_comparison variable usage ==="
rg -n "data_comparison" R --type r | head -20

echo ""
echo "=== MSstatsResponse usage ==="
rg -n "MSstatsResponse" R --type r

Repository: Vitek-Lab/MSstatsShiny

Length of output: 2792


🏁 Script executed:

#!/bin/bash
# View the data_comparison and related code context
sed -n '550,610p' R/module-statmodel-server.R

Repository: Vitek-Lab/MSstatsShiny

Length of output: 2147


🏁 Script executed:

#!/bin/bash
# Search for dataComparison function definition
echo "=== Search for dataComparison function definition ==="
rg -n "^dataComparison\s*=" R --type r

echo ""
echo "=== Alternative search pattern ==="
rg -n "dataComparison.*function" R --type r

echo ""
echo "=== Look in separate files ==="
find R -name "*.R" -type f | xargs grep -l "dataComparison"

Repository: Vitek-Lab/MSstatsShiny

Length of output: 375


🏁 Script executed:

#!/bin/bash
# View the dataComparison function
echo "=== dataComparison function ==="
sed -n '1212,1300p' R/utils.R

# Get file length to see how much more there is
echo ""
echo "=== File stats ==="
wc -l R/utils.R

Repository: Vitek-Lab/MSstatsShiny

Length of output: 1805


🏁 Script executed:

#!/bin/bash
# Find tmt_model and lf_model functions
echo "=== Search for tmt_model and lf_model ==="
rg -n "tmt_model\s*=" R --type r

echo ""
rg -n "lf_model\s*=" R --type r

echo ""
echo "=== Check if they're in utils.R ==="
grep -n "tmt_model\|lf_model" R/utils.R | head -20

Repository: Vitek-Lab/MSstatsShiny

Length of output: 855


🏁 Script executed:

#!/bin/bash
# View lf_model function
echo "=== lf_model function (starting at line 304) ==="
sed -n '304,350p' R/main_calculations.R

echo ""
echo "=== tmt_model function (starting at line 411) ==="
sed -n '411,460p' R/main_calculations.R

Repository: Vitek-Lab/MSstatsShiny

Length of output: 3559


🏁 Script executed:

#!/bin/bash
# Search for TODO comments related to response curves
echo "=== TODO comments in module-statmodel-server.R ==="
grep -n "Todo\|TODO" R/module-statmodel-server.R

echo ""
echo "=== Response curve related comments ==="
grep -n "response\|dose" R/module-statmodel-server.R | grep -i "todo\|fixme\|hack"

echo ""
echo "=== Check create_group_comparison_plot for response curve handling ==="
sed -n '258,310p' R/module-statmodel-server.R

Repository: Vitek-Lab/MSstatsShiny

Length of output: 3054


🏁 Script executed:

#!/bin/bash
# Check how the matrix is displayed in GUI
echo "=== Matrix display code (around line 578) ==="
sed -n '575,585p' R/module-statmodel-server.R

echo ""
echo "=== Check for any mode checks before calling analysis functions ==="
sed -n '559,572p' R/module-statmodel-server.R

echo ""
echo "=== Check build_response_curve_panel to understand UI flow ==="
rg -n "build_response_curve_panel" R --type r -A 10 | head -30

Repository: Vitek-Lab/MSstatsShiny

Length of output: 2339


Response curve matrix incompatibility requires conditional handling in downstream analysis pipeline.

The response-curve matrix building at lines 542-544 produces a data.frame with columns GROUP, dose_nM, and other metadata (from convertGroupToNumericDose), not the numeric contrast matrix expected by MSstats/MSstatsTMT.

Currently, this data.frame is passed directly to dataComparison() (line 559), which calls lf_model() or tmt_model() without mode detection. Both functions pass the matrix to MSstats::MSstatsContrastMatrix() (lf_model line 321, tmt_model line 424), which expects a numeric matrix. This will fail or produce incorrect results.

Additionally:

  • generate_analysis_code() has a TODO comment (line 336) indicating response curves are not yet handled; the current iteration logic (lines 348-356) assumes a numeric matrix.
  • create_group_comparison_plot() has a TODO (line 261) for dose response curve plotting and will not properly render results.
  • Six explicit TODOs throughout the module confirm the response curve feature is incomplete.

Add conditional mode checking before passing contrast$matrix to analysis and plotting functions, or implement the complete response curve pipeline. For the GUI, update the "Comparison matrix" panel text/tooltip to clarify that "Create response curves" displays dose/metadata mapping rather than classical contrast coefficients.

🤖 Prompt for AI Agents
In R/module-statmodel-server.R around lines 542-544, the code builds a
response-curve data.frame (GROUP, dose_nM, metadata) and assigns it to
contrast$matrix then passes it unchanged into the downstream analysis pipeline;
MSstats::MSstatsContrastMatrix expects a numeric contrast matrix so this will
break. Fix by adding a conditional branch: when
input[[NAMESPACE_STATMODEL$comparison_mode]] ==
CONSTANTS_STATMODEL$comparison_mode_response_curve, do not pass the
response-curve data.frame into dataComparison()/lf_model()/tmt_model(); instead
either (A) convert the response-curve mapping into the numeric contrast matrix
format required by MSstats::MSstatsContrastMatrix before assignment to
contrast$matrix, or (B) mark contrast$mode = "response_curve" and update
dataComparison(), lf_model(), tmt_model(), generate_analysis_code(), and
create_group_comparison_plot() to detect that mode and follow an alternate
response-curve analysis/rendering path (implementing the TODOs), and also update
the GUI "Comparison matrix" panel text/tooltip to state that "Create response
curves" yields a dose/metadata mapping rather than classical contrast
coefficients. Ensure all callers either receive a numeric matrix or explicitly
handle the response-curve mode to avoid passing a data.frame to
MSstatsContrastMatrix.

}

enable("calculate")
Expand Down
15 changes: 13 additions & 2 deletions R/statmodel-ui-comparisons.R
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ create_contrast_radio_buttons <- function(ns) {
"1. Define comparisons - contrast matrix",
class = "icon-wrapper",
icon("question-circle", lib = "font-awesome"),
div("Define what conditions you want to compare here.", class = "icon-tooltip")
div("Define what conditions you want to compare here", class = "icon-tooltip")
),
c(
"All possible pairwise comparisons" = CONSTANTS_STATMODEL$comparison_mode_all_pairwise,
"Compare all against one" = CONSTANTS_STATMODEL$comparison_mode_all_vs_one,
"Create custom pairwise comparisons" = CONSTANTS_STATMODEL$comparison_mode_custom_pairwise,
"Create custom non-pairwise comparisons" = CONSTANTS_STATMODEL$comparison_mode_custom_nonpairwise
"Create custom non-pairwise comparisons" = CONSTANTS_STATMODEL$comparison_mode_custom_nonpairwise,
"Create response curves" = CONSTANTS_STATMODEL$comparison_mode_response_curve
),
selected = character(0)
)
Expand Down Expand Up @@ -78,4 +79,14 @@ build_custom_nonpairwise_panel <- function(ns) {
actionButton(ns(NAMESPACE_STATMODEL$comparisons_submit), "Add"),
actionButton(ns(NAMESPACE_STATMODEL$comparisons_clear), "Clear matrix")
)
}

#' Create panel for configuring response curve metadata
#' @noRd
build_response_curve_panel <- function(ns) {
tagList(
h5("Set up response curve configuration:"),
actionButton(ns(NAMESPACE_STATMODEL$comparisons_submit), "Setup Metadata"),
actionButton(ns(NAMESPACE_STATMODEL$comparisons_clear), "Reset")
)
}
4 changes: 2 additions & 2 deletions R/statmodel-ui-headers.R
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ create_custom_styles <- function() {
create_header_section <- function() {
tagList(
headerPanel("Statistical modeling and inference"),
p("In this tab a statistical model is built in three steps:"),
p("(i) Create a contrast matrix with the correct Group comparisons,"),
p("In this tab, build your statistical model in three steps:"),
p("(i) Create a contrast matrix for a group comparison or set up a configuration for a response curve analysis,"),
p("(ii) generate the model and "),
p("(iii) view result plots."),
p("More info ", a("here", href="https://www.rdocumentation.org/packages/MSstats/versions/3.4.0/topics/groupComparisonPlots"))
Expand Down
11 changes: 11 additions & 0 deletions tests/testthat/test-statmodel-ui-options-contrasts.R
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,15 @@ test_that("build_custom_nonpairwise_panel creates correct UI structure", {
expect_true(grepl(">Clear matrix<", html, fixed = TRUE))
expect_s3_class(ui, "shiny.tag.list")
expect_length(ui, 5)
})

test_that("build_response_curve_panel creates correct UI structure", {
ns <- function(id) paste0("statmodel-", id)
ui <- build_response_curve_panel(ns)
html <- as.character(ui)
expect_true(grepl("<h5>Set up response curve configuration:</h5>", html, fixed = TRUE))
expect_true(grepl(NAMESPACE_STATMODEL$comparisons_submit, html, fixed = TRUE))
expect_true(grepl(NAMESPACE_STATMODEL$comparisons_clear, html, fixed = TRUE))
expect_s3_class(ui, "shiny.tag.list")
expect_length(ui, 3)
})
14 changes: 14 additions & 0 deletions tests/testthat/test-utils-statmodel-server.R
Original file line number Diff line number Diff line change
Expand Up @@ -374,4 +374,18 @@ test_that("get_contrast_panel_ui returns correct UI for each mode", {
expect_null(get_contrast_panel_ui(NULL, ns))
expect_null(get_contrast_panel_ui(character(0), ns))
expect_null(get_contrast_panel_ui("invalid", ns))
})


test_that("build_response_curve_matrix returns correct columns", {
condition_list = c("Dasatinib_001nM", "Dasatinib_001uM", "DMSO")

contrast <- list(matrix = NULL)
result <- build_response_curve_matrix(contrast, condition_list)

expect_equal(nrow(result), 3)
expect_equal(ncol(result), 3)
expect_true("GROUP" %in% colnames(result))
expect_true("drug" %in% colnames(result))
expect_true("dose_nM" %in% colnames(result))
})
Loading