Skip to content

feat(response-curve): Enable modeling and plotting of dose response curves#139

Merged
tonywu1999 merged 6 commits intodevelfrom
feature-response-curve-model
Dec 8, 2025
Merged

feat(response-curve): Enable modeling and plotting of dose response curves#139
tonywu1999 merged 6 commits intodevelfrom
feature-response-curve-model

Conversation

@tonywu1999
Copy link
Copy Markdown
Contributor

@tonywu1999 tonywu1999 commented Dec 8, 2025

Summary by CodeRabbit

  • New Features

    • Added a "Dose Response Curve" plot type with automated dose–response fitting and visualization.
    • Added protein and drug selection controls for dose–response views.
  • UI

    • Updated comparison-mode label text to reference "dose response" wording.
  • Tests

    • Updated UI and utility tests to cover the new plot options and matrix-building behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 8, 2025

Walkthrough

Adds dose‑response curve support: new MSstatsResponse imports, constants and UI for response-curve selection, updated matrix builder signatures, a new fitResponseCurves utility that calls MSstatsResponse, and server plumbing to prepare data and render response‑curve plots.

Changes

Cohort / File(s) Summary
Package exports & constants
NAMESPACE, R/constants.R
Added importFrom(MSstatsResponse, MSstatsPrepareDoseResponseFit), importFrom(MSstatsResponse, doseResponseFit), importFrom(MSstatsResponse, visualizeResponseProtein) in NAMESPACE; added visualization_which_protein to NAMESPACE_STATMODEL and plot_type_response_curve to CONSTANTS_STATMODEL; removed visualization_comparison_which_protein.
Server: statmodel module
R/module-statmodel-server.R
Updated build_response_curve_matrix signature to accept condition_list only; extended render_group_comparison_plot_inputs signature to accept condition_list; switched UI ID usage to visualization_which_protein; added response‑curve UI branch (drug selector) and server flow to call fitResponseCurves and visualizeResponseProtein.
UI: visualization options
R/statmodel-ui-options-visualization.R
Added "Dose Response Curve" plot type option; replaced UI ID references to visualization_which_protein; added create_response_curve_options(ns) to render protein/drug selectors for response curves.
Utilities: dose-response fitting
R/utils.R
Added fitResponseCurves(statmodel_input, matrix, input_data) — merges data with matrix, calls MSstatsPrepareDoseResponseFit and doseResponseFit, and returns results as list(ComparisonResult = ...).
Tests & small UI text change
tests/..., R/statmodel-ui-comparisons.R
Updated tests to expect new UI ID (visualization_which_protein); changed comparison-mode label text to "Create dose response curves".

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant UI as Shiny UI
    participant Server as Server Logic
    participant Utils as fitResponseCurves
    participant MSR as MSstatsResponse

    User->>UI: choose "Dose Response Curve" and select protein/drug
    UI->>Server: inputs (plot_type, protein, drug, condition_list)
    Server->>Server: build_response_curve_matrix(condition_list)
    Server->>Utils: fitResponseCurves(statmodel_input, matrix, data)
    activate Utils
    Utils->>MSR: MSstatsPrepareDoseResponseFit(prepared_data, colmap...)
    MSR-->>Utils: prepared dose-response dataset
    Utils->>MSR: doseResponseFit(prepared_data, params)
    MSR-->>Utils: fitted response results
    Utils-->>Server: list(ComparisonResult = results)
    deactivate Utils
    Server->>MSR: visualizeResponseProtein(results, protein, drug)
    MSR-->>Server: plot object / rendering output
    Server->>UI: render dose-response plot
    UI->>User: display plot
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay special attention to:
    • R/module-statmodel-server.R — signature changes and all call sites for build_response_curve_matrix and render_group_comparison_plot_inputs.
    • R/utils.R — correctness of column mappings and parameter choices passed to MSstatsPrepareDoseResponseFit and doseResponseFit.
    • NAMESPACE changes — ensure imports match package availability and version compatibility.
    • Tests — updated tests referencing new UI IDs and matrix behavior.

Possibly related PRs

Suggested reviewers

  • sszvetecz

"I'm a rabbit in the codewood, quick and spry,
Dose curves hop in rows, under moonlit sky.
Proteins, drugs, and plots align in tune,
Fitters hum, visuals bloom — a developer's boon.
🐇📈💊"

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(response-curve): Enable modeling and plotting of dose response curves' clearly and specifically describes the main change: enabling a new feature for dose response curve modeling and visualization.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature-response-curve-model

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Dec 8, 2025

Failed to generate code suggestions for PR

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
R/utils.R (1)

1244-1244: Unused parameter statmodel_input.

The statmodel_input parameter is passed to the function but never used. Either remove it or document why it's needed for future use.

-fitResponseCurves <- function(statmodel_input, matrix, input_data) {
+fitResponseCurves <- function(matrix, input_data) {
R/module-statmodel-server.R (1)

266-282: TODO comment should be addressed or tracked.

Line 268-269 contains a TODO comment indicating that build_response_curve_matrix(condition_list())$drug should be changed to use the drug column from a user-defined matrix. This suggests the current implementation may not handle custom matrix configurations correctly.

Would you like me to open an issue to track implementing user-defined matrix support for the drug column selection?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bfc2ba3 and 0b5c092.

📒 Files selected for processing (5)
  • NAMESPACE (1 hunks)
  • R/constants.R (2 hunks)
  • R/module-statmodel-server.R (12 hunks)
  • R/statmodel-ui-options-visualization.R (3 hunks)
  • R/utils.R (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (6)
R/statmodel-ui-options-visualization.R (2)

27-28: LGTM!

The addition of "Response Curve" to the plot type selector is properly integrated with the corresponding constant plot_type_response_curve.


101-109: LGTM!

The create_response_curve_options function follows the established pattern of other option functions and correctly references the namespace constants for protein and drug selection.

R/constants.R (1)

18-18: LGTM!

The new constants for response curve visualization are properly added and follow the existing naming conventions. The namespace and constant additions align with their usage in the UI and server modules.

Also applies to: 26-26, 39-40

R/module-statmodel-server.R (2)

208-211: LGTM!

The simplified build_response_curve_matrix function correctly returns a data.frame with GROUP and metadata columns derived from convertGroupToNumericDose.


595-600: Verify fitResponseCurves receives expected parameters.

The call passes input as statmodel_input, but as noted in R/utils.R, this parameter is unused. If the unused parameter is intentional for future use, consider documenting this; otherwise, align the call with the simplified signature.

NAMESPACE (1)

51-54: MSstatsResponse is properly declared as a dependency.

The package is listed in the DESCRIPTION file (line 30) under Imports, so the importFrom directives in the NAMESPACE file are backed by a proper dependency declaration and will not cause installation issues.

Comment on lines +645 to +670
} else if (input[[NAMESPACE_STATMODEL$visualization_plot_type]] ==
CONSTANTS_STATMODEL$plot_type_response_curve) {
matrix = matrix_build()
protein_level_data <- merge(preprocess_data()$ProteinLevelData, matrix, by = "GROUP")
dia_prepared <- MSstatsPrepareDoseResponseFit(
data = protein_level_data,
dose_column = "dose_nM",
drug_column = "drug",
protein_column = "Protein",
log_abundance_column = "LogIntensities",
transform_nM_to_M = TRUE
)
output$comp_plots = renderPlot({
visualizeResponseProtein(
data = dia_prepared,
protein_name = input[[NAMESPACE_STATMODEL$visualization_which_protein]],
drug_name = input[[NAMESPACE_STATMODEL$visualization_response_curve_which_drug]],
ratio_response = TRUE,
show_ic50 = TRUE,
add_ci = TRUE,
transform_dose = TRUE,
n_samples = 1000,
increasing = FALSE
)
})
op = plotOutput(ns("comp_plots"))
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

Code duplication and inconsistent parameter usage between fitting and visualization.

The MSstatsPrepareDoseResponseFit call here (lines 649-656) duplicates the logic in fitResponseCurves (R/utils.R lines 1246-1253). Additionally, ratio_response = TRUE at line 662 differs from ratio_response = FALSE in doseResponseFit (utils.R line 1258), which may cause visualization/fit mismatches.

Consider extracting the prepared data from data_comparison() or creating a shared helper to ensure consistency.

        } else if (input[[NAMESPACE_STATMODEL$visualization_plot_type]] == 
                   CONSTANTS_STATMODEL$plot_type_response_curve) {
-          matrix = matrix_build()
-          protein_level_data <- merge(preprocess_data()$ProteinLevelData, matrix, by = "GROUP")
-          dia_prepared <- MSstatsPrepareDoseResponseFit(
-            data = protein_level_data,
-            dose_column = "dose_nM",
-            drug_column = "drug",
-            protein_column = "Protein",
-            log_abundance_column = "LogIntensities",
-            transform_nM_to_M = TRUE  
-          )
+          # Reuse the prepared data from data_comparison() to ensure consistency
+          dia_prepared <- data_comparison()$PreparedData  # Requires fitResponseCurves to return this
           output$comp_plots = renderPlot({ 
             visualizeResponseProtein(
               data = dia_prepared,
               protein_name = input[[NAMESPACE_STATMODEL$visualization_which_protein]],
               drug_name = input[[NAMESPACE_STATMODEL$visualization_response_curve_which_drug]],
-              ratio_response = TRUE,
+              ratio_response = FALSE,  # Match doseResponseFit parameter
               show_ic50 = TRUE,

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In R/module-statmodel-server.R around lines 645 to 670, the
MSstatsPrepareDoseResponseFit call duplicates logic from fitResponseCurves
(R/utils.R lines ~1246-1253) and uses ratio_response = TRUE which conflicts with
doseResponseFit's ratio_response = FALSE; replace the inline preparation by
reusing the same prepared object used by fitting (e.g., pull the prepared data
from data_comparison() or call the shared helper used by fitResponseCurves),
remove the duplicated MSstatsPrepareDoseResponseFit block, and ensure the
visualization call uses the same ratio_response value and other parameters as
the fitting pipeline so fit and plot are consistent.

Comment thread R/utils.R
Comment on lines +1245 to +1260
protein_level_data <- merge(input_data$ProteinLevelData, matrix, by = "GROUP")
dia_prepared <- MSstatsPrepareDoseResponseFit(
data = protein_level_data,
dose_column = "dose_nM",
drug_column = "drug",
protein_column = "Protein",
log_abundance_column = "LogIntensities",
transform_nM_to_M = TRUE
)
response_results <- doseResponseFit(
data = dia_prepared,
increasing = FALSE,
transform_dose = TRUE,
ratio_response = FALSE
)
return(list(ComparisonResult = response_results))
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

Hardcoded column names may cause runtime errors with different data formats.

The column names "dose_nM", "drug", "Protein", and "LogIntensities" are hardcoded. If user data doesn't contain these exact columns after the merge, MSstatsPrepareDoseResponseFit will fail. Consider adding validation or making column names configurable.

Additionally, there's no error handling if the merge produces an empty result or if required columns are missing:

 fitResponseCurves <- function(statmodel_input, matrix, input_data) {
   protein_level_data <- merge(input_data$ProteinLevelData, matrix, by = "GROUP")
+  
+  required_cols <- c("dose_nM", "drug", "Protein", "LogIntensities")
+  missing_cols <- setdiff(required_cols, colnames(protein_level_data))
+  if (length(missing_cols) > 0) {
+    stop("Missing required columns after merge: ", paste(missing_cols, collapse = ", "))
+  }
+  
   dia_prepared <- MSstatsPrepareDoseResponseFit(

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
tests/testthat/test-utils-statmodel-server.R (1)

380-391: Consider slightly strengthening the build_response_curve_matrix test

Using the new build_response_curve_matrix(condition_list) signature looks correct, and the current expectations guard basic structure, but they won’t catch regressions in the returned object type or column types.

You could add a couple of low‑assumption checks, e.g. that the result is a data.frame and that dose_nM is numeric:

   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))
+  expect_s3_class(result, "data.frame")
+  expect_true(is.numeric(result$dose_nM))

This keeps the test aligned with the new API while giving you earlier signal if the underlying representation changes unexpectedly.

tests/testthat/test-statmodel-ui-options-visualization.R (1)

28-34: Updated namespace expectation is correct; extend tests for the new response‑curve UI

Switching the comparison plot test to NAMESPACE_STATMODEL$visualization_which_protein matches the new shared ID in the visualization options module.

Given you’ve added a "Dose Response Curve" plot type and create_response_curve_options(), it would be good to expand this test file to cover those as well. For example:

  1. Assert that the new plot type label is present:
 test_that("All possible options in create_plot_type_selector", {
   ns <- NS("test_module")
   result <- create_plot_type_selector(ns)
   ui_html <- htmltools::renderTags(result)$html
   expect_true(grepl("Volcano Plot", ui_html),
               info = "Volcano Plot option should be present")
   expect_true(grepl("Heatmap", ui_html),
               info = "Heatmap option should be present")
   expect_true(grepl("Comparison Plot", ui_html),
               info = "Comparison Plot should be present")
+  expect_true(grepl("Dose Response Curve", ui_html),
+              info = "Dose Response Curve option should be present")
 })
  1. Add a focused test for the response‑curve options UI:
 test_that("Correct elements are present in create_comparison_plot_options", {
   ns <- NS("test_module")
   result <- create_comparison_plot_options(ns)
   ui_html <- htmltools::renderTags(result)$html
   expect_true(grepl(NAMESPACE_STATMODEL$visualization_which_protein, ui_html),
               info = "Which protein namespace should be present")
 })
+
+test_that("Correct elements are present in create_response_curve_options", {
+  ns <- NS("test_module")
+  result <- create_response_curve_options(ns)
+  ui_html <- htmltools::renderTags(result)$html
+  expect_true(grepl(NAMESPACE_STATMODEL$visualization_which_protein, ui_html),
+              info = "Which protein namespace should be present")
+  expect_true(grepl(NAMESPACE_STATMODEL$visualization_response_curve_which_drug, ui_html),
+              info = "Which drug namespace should be present")
+})

That will lock in the new feature’s UI contract and catch accidental ID/name changes.

Also applies to: 51-62

R/statmodel-ui-options-visualization.R (1)

24-32: New plot type and response‑curve options are wired cleanly; add tests to guard the new IDs

The additions here look consistent and low‑risk:

  • create_plot_type_selector() now exposes "Comparison Plot" and "Dose Response Curve" via the corresponding CONSTANTS_STATMODEL$plot_type_* values, keeping the logic in terms of constants.
  • create_comparison_plot_options() and create_response_curve_options() both use the shared visualization_which_protein ID, which matches the updated namespace used in tests and keeps the server side from having to handle multiple protein‑selector IDs.
  • create_response_curve_options() introduces visualization_response_curve_which_drug, giving a clear separation between protein and drug selection.

To make this robust against regressions, I’d rely on tests like the ones suggested in tests/testthat/test-statmodel-ui-options-visualization.R (checking for the "Dose Response Curve" label and both visualization_which_protein and visualization_response_curve_which_drug in the rendered HTML). That keeps the UI contract enforced without changing the implementation here.

Also applies to: 63-67, 102-109

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0b5c092 and 9682706.

📒 Files selected for processing (4)
  • R/statmodel-ui-comparisons.R (1 hunks)
  • R/statmodel-ui-options-visualization.R (3 hunks)
  • tests/testthat/test-statmodel-ui-options-visualization.R (1 hunks)
  • tests/testthat/test-utils-statmodel-server.R (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
R/statmodel-ui-comparisons.R (1)

28-34: Radio label wording change looks good

Switching the label to "Create dose response curves" keeps behavior identical while making the intent clearer and consistent with the “Dose Response Curve” plot type elsewhere in the UI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant