Skip to content

Refactor loadpage: move converter panels to server-side show/hide#217

Open
swaraj-neu wants to merge 4 commits into
develfrom
MSstatsShiny/work/20260619_loadpage_refactor
Open

Refactor loadpage: move converter panels to server-side show/hide#217
swaraj-neu wants to merge 4 commits into
develfrom
MSstatsShiny/work/20260619_loadpage_refactor

Conversation

@swaraj-neu

@swaraj-neu swaraj-neu commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Motivation and context

Refactor the loadpage flow to move UI show/hide logic out of client-side conditionalPanel handling and into server-controlled visibility, while splitting the module server code into smaller helper files. The change is intended to preserve behavior while improving structure and maintainability.

Changes

  • Added NAMESPACE_LOADPAGE to centralize loadpage-related container/input ids.
  • Split the loadpage server logic into focused helpers for:
    • preview loading and DIANN/PTM preview handling
    • proceed-button validation
    • data loading and download export
    • summary/condition-metadata handling
    • UI visibility observers
    • converter-specific UI rendering
  • Reworked many loadpage UI sections to use server-controlled hidden containers instead of static conditionalPanel logic, including:
    • sample dataset descriptions
    • label-free type selection
    • standard/MSstats/Skyline uploads
    • PTM, MaxQuant, FragPipe, and related option panels
    • TMT options
    • DIANN anomaly/run-order controls
    • OpenSWATH M-score controls
  • Updated loadpageServer() to act as an orchestrator that wires the new helpers together and returns a smaller public interface.
  • Added helper logic for:
    • DIANN preview auto-detection and mismatch warnings
    • PTM modification-id preview selection
    • seeded/default protein-id behavior when switching TMT converters
    • visibility predicates for each UI cluster
    • disabling incompatible converter choices
  • Cleaned up comments, naming, and summary placement as part of the refactor.

Tests

  • Added/updated tests covering:
    • visibility predicate truth tables
    • NAMESPACE_LOADPAGE id literals
    • loadpage_seed_proteinid() state-preservation behavior
    • hidden-container rendering for migrated UI sections
    • remaining Spectronaut carveout conditionalPanel cases
    • DIANN big-file gating behavior
    • removal of old static conditionalPanel expectations where server-side visibility now applies

Coding guidelines violated

  • None reported.

- move converter panels to server-side show/hide
- split the server file into smaller helpers
@swaraj-neu swaraj-neu requested a review from tonywu1999 June 25, 2026 16:09
@swaraj-neu swaraj-neu self-assigned this Jun 25, 2026
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The loadpageServer module is decomposed into six registered helper files (register_loadpage_preview, register_loadpage_visibility_observers, register_loadpage_converter_ui, register_loadpage_proceed_validation, register_loadpage_data_loaders, register_loadpage_summary). Static conditionalPanel UI is replaced with permanently-mounted shinyjs::hidden divs keyed by a new NAMESPACE_LOADPAGE constant, with server-side toggle observers.

Changes

Loadpage Phase 2 split

Layer / File(s) Summary
Namespace constants and visibility predicates
R/constants.R, R/loadpage-server-converter-options-panel.R
NAMESPACE_LOADPAGE exports all loadpage container string IDs; pure loadpage_show_* predicate functions and loadpage_seed_proteinid define boolean gating rules for every panel.
Hidden container UI migration
R/module-loadpage-ui.R
Sample descriptions, upload sections, PTM inputs, DIANN/Umpire, TMT options, and quality-filtering panels migrate from conditionalPanel to permanently-mounted shinyjs::hidden divs keyed by NAMESPACE_LOADPAGE IDs; Spectronaut carveouts remain as conditionalPanel.
Visibility observers and converter UI
R/loadpage-server-converter-options-panel.R
register_loadpage_visibility_observers wires shinyjs::toggle observers to predicates for all hidden containers and provides the TMT renderUI slot; register_loadpage_converter_ui renders Spectronaut/DIANN UI fragments and disables incompatible converter radios.
Preview loading and DIANN detection
R/loadpage-server-preview.R
register_loadpage_preview reads first-100-row previews for Metamorpheus PTM and DIANN uploads, auto-detects DIANN 2.0+ format, warns on user/detected mismatches, and renders the modification-ID selector.
Proceed1 gating
R/loadpage-server-proceed-validation.R
register_loadpage_proceed_validation disables proceed1 on every reactive run then enables it only when per-format required inputs are present, covering LType/TMT/PTM modes and big-file path checks.
Data loading and download handler
R/loadpage-server-data-loaders.R
register_loadpage_data_loaders defines per-format file reactives, get_data triggered by proceed1 with error handling, download_msstats_format as CSV or ZIP, and get_data_code from calculate.
Summary cluster and orchestration
R/loadpage-server-summary.R, R/module-loadpage-server.R
register_loadpage_summary initializes condition_metadata for protein_turnover and chemoproteomics templates, renders editable DT and summary tables after proceed1, handles proceed2 navigation; loadpageServer is reduced to wiring the six helpers and returning input/getData/getConditionMetadata.
Predicate and UI tests
tests/testthat/test-loadpage-server-rendering.R, tests/testthat/test-module-loadpage-ui.R
Truth-table tests cover all loadpage_show_* predicates and loadpage_seed_proteinid switching; UI tests verify hidden container divs are present and conditionalPanel markup is absent for migrated panels.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Vitek-Lab/MSstatsShiny#184: Both PRs implement the Metamorpheus PTM modification-ID UI via mod_id_meta_ui and preview_data, with the __other__ custom input path.
  • Vitek-Lab/MSstatsShiny#212: The main PR explicitly preserves Spectronaut large-file anomaly/run-order controls as conditionalPanel carveouts, directly overlapping with PR #212's addition of those controls.
  • Vitek-Lab/MSstatsShiny#215: Both PRs implement the download_msstats_format downloadHandler and its enable/disable behavior, with the main PR refactoring it into register_loadpage_data_loaders.

Suggested labels

Review effort 3/5

Suggested reviewers

  • tonywu1999

🐇 Six helpers now share the load,
Hidden divs line every road.
Predicates true, predicates false—
The rabbit checks each toggle clause!
proceed1 gates with careful care,
Phase 2 splits the work with flair. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main refactor: moving loadpage converter panels to server-side show/hide.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch MSstatsShiny/work/20260619_loadpage_refactor

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
R/module-loadpage-ui.R (1)

858-878: 🎯 Functional Correctness | 🟠 Major

Make the conditionalPanel conditions respect the module namespace.

Lines 858 and 872 use hardcoded IDs (loadpage-filetype) for the condition attribute, while the inputs are namespaced with ns(). This mismatch causes the conditional logic to fail whenever the module is instantiated with a prefix other than the literal string "loadpage".

The logic must be updated to generate the ID string for the condition using ns.

Suggested Fix

Replace static ID strings with dynamically constructed ones using sprintf and ns.

     conditionalPanel(
-      condition = "input['loadpage-filetype'] == 'spec'",
+      condition = sprintf("input['%s'] == 'spec'", ns("filetype")),
       checkboxInput(ns("calculate_anomaly_scores"),
@@
       conditionalPanel(
-        condition = "input['loadpage-calculate_anomaly_scores']",
+        condition = sprintf("input['%s']", ns("calculate_anomaly_scores")),
         fileInput(ns("run_order_file"),

Note: Ensure "filetype" matches the actual ID passed to fileInput or the relevant global ID if one exists in this scope. If 'loadpage-filetype' was a hard reference to a global or parent container ID that doesn't follow standard module namespacing, verify if a global ns exists, but standard practice suggests ns("filetype") should map to the correct conditionalPanel target if that input is also namespaced or if a global ns is available.

If the inputs inside are namespaced, the condition checking them must also use that namespace to ensure they are visible.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@R/module-loadpage-ui.R` around lines 858 - 878, The conditionalPanel checks
are using hardcoded input IDs instead of the module namespace, so the visibility
logic breaks for non-default module prefixes. Update the condition strings in
the loadpage UI block to build the namespaced ID from ns for the filetype and
calculate_anomaly_scores inputs, and verify the nested run_order_file panel
still targets the correct namespaced control within the same module. Use the
existing ns helper in the module UI code so the condition matches the actual
input IDs created by checkboxInput and fileInput.
🧹 Nitpick comments (2)
R/loadpage-server-summary.R (2)

186-189: 🩺 Stability & Availability | 🔵 Trivial

Register proceed2 only once.

The onclick("proceed2", ...) call is nested inside the proceed1 handler, causing the proceed2 event listener to be re-registered every time proceed1 is clicked. Move the proceed2 block to the same scope level as the proceed1 block so it initializes only once.

    onclick("proceed2", {
      updateTabsetPanel(session = parent_session, inputId = "tablist",
                        selected = "DataProcessing")
    })
  })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@R/loadpage-server-summary.R` around lines 186 - 189, The proceed2 click
handler is being registered inside the proceed1 callback, so it gets added again
on every proceed1 click; move the onclick("proceed2", ...) registration out to
the same scope as onclick("proceed1", ...) in loadpage-server-summary.R so it
initializes only once. Use the existing proceed1/proceed2 handler blocks and
keep the updateTabsetPanel(session = parent_session, inputId = "tablist",
selected = "DataProcessing") behavior unchanged.

190-228: 🎯 Functional Correctness | 🔵 Trivial

Inconsistent ID referencing breaks module reusability

Although loadpageServer is currently called with the specific ID "loadpage", the code uses ns for dynamic ID generation in other places but hard-codes "loadpage-"" in CSS selectors and conditionalPanel` conditions. This renders the module brittle and unusable if renamed or wrapped in another namespace later.

Update the references to use the namespace function consistently:

  1. CSS Selector: Generate the ID dynamically instead of hard-coding the prefix:
    tags$style(HTML(sprintf('#%s{background-color:orange}', ns("proceed2"))))
  2. conditionalPanel: Pass ns = ns to handle the input ID translation automatically:
    conditionalPanel(condition = "input.BIO !== 'PTM'", ns = ns, ...)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@R/loadpage-server-summary.R` around lines 190 - 228, The UI in renderUI for
summary_tables hard-codes the module prefix in the CSS selector and
conditionalPanel logic, which breaks reuse if loadpageServer is renamed or
nested. Update the proceed2 style in tags$head to use ns("proceed2") instead of
a fixed selector, and pass ns = ns into each conditionalPanel so input IDs are
translated automatically. Keep the existing renderUI structure and identifiers
like summary_tables, proceed2, and conditionalPanel, but make all ID references
namespace-aware.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@R/loadpage-server-preview.R`:
- Around line 35-59: The DIANN preview resolution in main_data_file() only
handles input$dianndata, so local large-file uploads never reach preview_data()
and skip the diann_2plus auto-detect/mismatch-warning flow. Update
main_data_file() to also consider the local_big_diann_path state used by
register_loadpage_proceed_validation(), and for that branch return a preview
source shaped like {datapath, name} so preview_data() can load it the same way
as other inputs.
- Around line 93-105: The preview handling in loadpage-server-preview.R treats
every non-2.0+ result from .is_diann_2plus() as DIANN 1.x, which is incorrect
for unknown formats. Update the logic around last_detected_diann_format(),
updateCheckboxInput(), and the showNotification calls so that only a confirmed
positive detection maps to “DIANN 2.0+”, a confirmed legacy fragment column maps
to “DIANN 1.x”, and the no-match case leaves the state unset/unchanged and shows
an unknown-format message instead. Apply the same distinction in the mismatch
warning path referenced by the same preview update flow.

In `@R/loadpage-server-rendering.R`:
- Around line 855-901: The radio-button selectors in the observe block are
hardcoded to the global loadpage-filetype name, so they won’t work correctly
when the module ID changes or multiple instances exist. Update the selector
logic in this observe() path to use session$ns with NAMESPACE_LOADPAGE$filetype
so the [type=radio][name=...] lookups are module-scoped, and keep the
value-based disable calls tied to that namespaced radio group.

In `@R/loadpage-server-summary.R`:
- Around line 78-81: The proceed1 click handler currently shows summary_tables
even when get_data() fails and returns NULL. Update the proceed1 callback to
capture the result of get_data() and only continue to get_annot() and
shinyjs::show("summary_tables") when a valid dataset is loaded; otherwise stop
early and keep the Next step hidden. Use the proceed1 onclick handler and
get_data() as the key points to locate the fix.
- Around line 85-97: Surface failures in the protein_turnover metadata setup
instead of swallowing them in the tryCatch around get_data and
condition_metadata. In the app_template() == TEMPLATES$protein_turnover branch,
update the error handler to mirror the chemoproteomics path by logging a warning
or message with the caught error e, so loadpage-server-summary.R makes
initialization issues visible while still allowing the UI to render.

In `@R/module-loadpage-server.R`:
- Around line 13-15: The helper-count documentation is outdated in the
module-loadpage server orchestrator. Update the roxygen text and the related
section header in the code that documents the helper registrations so they say
six helpers instead of five, keeping the wording in sync with the actual
registration order in the orchestrator and the final return list.

---

Outside diff comments:
In `@R/module-loadpage-ui.R`:
- Around line 858-878: The conditionalPanel checks are using hardcoded input IDs
instead of the module namespace, so the visibility logic breaks for non-default
module prefixes. Update the condition strings in the loadpage UI block to build
the namespaced ID from ns for the filetype and calculate_anomaly_scores inputs,
and verify the nested run_order_file panel still targets the correct namespaced
control within the same module. Use the existing ns helper in the module UI code
so the condition matches the actual input IDs created by checkboxInput and
fileInput.

---

Nitpick comments:
In `@R/loadpage-server-summary.R`:
- Around line 186-189: The proceed2 click handler is being registered inside the
proceed1 callback, so it gets added again on every proceed1 click; move the
onclick("proceed2", ...) registration out to the same scope as
onclick("proceed1", ...) in loadpage-server-summary.R so it initializes only
once. Use the existing proceed1/proceed2 handler blocks and keep the
updateTabsetPanel(session = parent_session, inputId = "tablist", selected =
"DataProcessing") behavior unchanged.
- Around line 190-228: The UI in renderUI for summary_tables hard-codes the
module prefix in the CSS selector and conditionalPanel logic, which breaks reuse
if loadpageServer is renamed or nested. Update the proceed2 style in tags$head
to use ns("proceed2") instead of a fixed selector, and pass ns = ns into each
conditionalPanel so input IDs are translated automatically. Keep the existing
renderUI structure and identifiers like summary_tables, proceed2, and
conditionalPanel, but make all ID references namespace-aware.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 958ed1a8-ecd3-49e6-8a81-78ef434b9bb1

📥 Commits

Reviewing files that changed from the base of the PR and between e4342c9 and c21f5f6.

📒 Files selected for processing (10)
  • R/constants.R
  • R/loadpage-server-data-loaders.R
  • R/loadpage-server-preview.R
  • R/loadpage-server-proceed-validation.R
  • R/loadpage-server-rendering.R
  • R/loadpage-server-summary.R
  • R/module-loadpage-server.R
  • R/module-loadpage-ui.R
  • tests/testthat/test-loadpage-server-rendering.R
  • tests/testthat/test-module-loadpage-ui.R

Comment on lines +35 to +59
main_data_file <- reactive({
req(input$filetype)
if (input$BIO == "PTM") {
switch(input$filetype,
"meta" = input$ptm_input,
# TODO: "maxq" = input$ptm_input,
# TODO: "PD" = input$ptm_input,
# TODO: "spec" = input$ptm_input,
# TODO: "sky" = input$ptm_input,
# TODO: "phil" = input$ptmdata,
# TODO: "msstats" = input$msstatsptmdata,
NULL
)
} else {
switch(input$filetype,
# TODO: Map remaining non-PTM file types when preview features are needed
"prog" =, "PD" =, "open" =, "openms" =, "spmin" =, "phil" =, "meta" = input$data,
"msstats" = input$msstatsdata,
"sky" = input$skylinedata,
"spec" = input$specdata,
"diann" = input$dianndata,
"maxq" = input$evidence,
NULL
)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Wire DIANN large-file mode into preview resolution.

register_loadpage_proceed_validation() enables the DIANN large-file path flow in R/loadpage-server-proceed-validation.R Lines 68-73, but main_data_file() here only resolves input$dianndata. In local large-file mode that input stays NULL, so preview_data() never loads and the diann_2plus auto-detect / mismatch-warning path is skipped for a supported upload flow. Please thread local_big_diann_path into this helper and synthesize a {datapath, name}-shaped preview source for that branch too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@R/loadpage-server-preview.R` around lines 35 - 59, The DIANN preview
resolution in main_data_file() only handles input$dianndata, so local large-file
uploads never reach preview_data() and skip the diann_2plus
auto-detect/mismatch-warning flow. Update main_data_file() to also consider the
local_big_diann_path state used by register_loadpage_proceed_validation(), and
for that branch return a preview source shaped like {datapath, name} so
preview_data() can load it the same way as other inputs.

Comment on lines +93 to +105
is_2plus <- .is_diann_2plus(preview)
previous <- last_detected_diann_format()
# Only update and notify when the detected state actually changes
if (is.null(previous) || previous != is_2plus) {
updateCheckboxInput(session, "diann_2plus", value = is_2plus)
if (is_2plus) {
showNotification("Detected DIANN 2.0+ format (per-fragment columns).",
type = "message", duration = 5)
} else {
showNotification("Detected DIANN 1.x format (legacy fragment column).",
type = "message", duration = 5)
}
last_detected_diann_format(is_2plus)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Don’t equate “not 2.0+” with “definitely DIANN 1.x”.

.is_diann_2plus() only answers the positive case. A preview with neither numbered fragment columns nor the legacy fragment column also returns FALSE, so this code auto-unchecks the box and tells the user “DIANN 1.x” even when the format is actually unknown; the mismatch warning below repeats the same assumption.

Suggested direction
-    is_2plus <- .is_diann_2plus(preview)
+    cols <- names(preview)
+    is_2plus <- .is_diann_2plus(preview)
+    is_legacy <- any(cols %in% c("Fragment.Quant.Corrected", "FragmentQuantCorrected"))
     previous <- last_detected_diann_format()
     # Only update and notify when the detected state actually changes
-    if (is.null(previous) || previous != is_2plus) {
-      updateCheckboxInput(session, "diann_2plus", value = is_2plus)
-      if (is_2plus) {
+    if (is_2plus && (is.null(previous) || !isTRUE(previous))) {
+      updateCheckboxInput(session, "diann_2plus", value = TRUE)
         showNotification("Detected DIANN 2.0+ format (per-fragment columns).",
                          type = "message", duration = 5)
-      } else {
+      last_detected_diann_format(TRUE)
+    } else if (is_legacy && (is.null(previous) || !identical(previous, FALSE))) {
+      updateCheckboxInput(session, "diann_2plus", value = FALSE)
         showNotification("Detected DIANN 1.x format (legacy fragment column).",
                          type = "message", duration = 5)
-      }
-      last_detected_diann_format(is_2plus)
+      last_detected_diann_format(FALSE)
+    } else if (!is_2plus && !is_legacy) {
+      showNotification("Could not determine whether the preview is DIANN 1.x or 2.0+.",
+                       type = "warning", duration = 5)
     }

Also applies to: 114-121

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@R/loadpage-server-preview.R` around lines 93 - 105, The preview handling in
loadpage-server-preview.R treats every non-2.0+ result from .is_diann_2plus() as
DIANN 1.x, which is incorrect for unknown formats. Update the logic around
last_detected_diann_format(), updateCheckboxInput(), and the showNotification
calls so that only a confirmed positive detection maps to “DIANN 2.0+”, a
confirmed legacy fragment column maps to “DIANN 1.x”, and the no-match case
leaves the state unset/unchanged and shows an unknown-format message instead.
Apply the same distinction in the mismatch warning path referenced by the same
preview update flow.

Comment on lines +855 to +901
observe({
if ((input$BIO == "Protein" || input$BIO == "Peptide") && input$DDA_DIA == "LType") {
runjs("$('[type=radio][name=loadpage-filetype]:disabled').parent().parent().parent().find('div.radio').css('opacity', 1)")
enable("filetype")
disable(selector = "[type=radio][value=spmin]")
runjs("$.each($('[type=radio][name=loadpage-filetype]:disabled'), function(_, e){ $(e).parent().parent().css('opacity', 0.4) })")

} else if ((input$BIO == "Protein" || input$BIO == "Peptide") && input$DDA_DIA == "TMT") {
runjs("$('[type=radio][name=loadpage-filetype]:disabled').parent().parent().parent().find('div.radio').css('opacity', 1)")
enable("filetype")
disable(selector = "[type=radio][value=sky]")
disable(selector = "[type=radio][value=prog]")
disable(selector = "[type=radio][value=spec]")
disable(selector = "[type=radio][value=open]")
disable(selector = "[type=radio][value=ump]")
disable(selector = "[type=radio][value=diann]")
disable(selector = "[type=radio][value=meta]")
runjs("$.each($('[type=radio][name=loadpage-filetype]:disabled'), function(_, e){ $(e).parent().parent().css('opacity', 0.4) })")

} else if (input$BIO == "PTM" && input$DDA_DIA == "LType") {
runjs("$('[type=radio][name=loadpage-filetype]:disabled').parent().parent().parent().find('div.radio').css('opacity', 1)")
enable("filetype")
# disable(selector = "[type=radio][value=sky]")
disable(selector = "[type=radio][value=prog]")
disable(selector = "[type=radio][value=PD]")
disable(selector = "[type=radio][value=openms]")
disable(selector = "[type=radio][value=spmin]")
disable(selector = "[type=radio][value=open]")
disable(selector = "[type=radio][value=ump]")
disable(selector = "[type=radio][value=phil]")
disable(selector = "[type=radio][value=diann]")

runjs("$.each($('[type=radio][name=loadpage-filetype]:disabled'), function(_, e){ $(e).parent().parent().css('opacity', 0.4) })")
} else if (input$BIO == "PTM" && input$DDA_DIA == "TMT") {
runjs("$('[type=radio][name=loadpage-filetype]:disabled').parent().parent().parent().find('div.radio').css('opacity', 1)")
enable("filetype")
disable(selector = "[type=radio][value=prog]")
disable(selector = "[type=radio][value=openms]")
disable(selector = "[type=radio][value=spec]")
disable(selector = "[type=radio][value=open]")
disable(selector = "[type=radio][value=ump]")
disable(selector = "[type=radio][value=spmin]")
disable(selector = "[type=radio][value=diann]")
disable(selector = "[type=radio][value=sky]")
disable(selector = "[type=radio][value=meta]")

runjs("$.each($('[type=radio][name=loadpage-filetype]:disabled'), function(_, e){ $(e).parent().parent().css('opacity', 0.4) })")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n R/loadpage-server-rendering.R | head -n 920

Repository: Vitek-Lab/MSstatsShiny

Length of output: 41220


🏁 Script executed:

grep -n "radio.*filetype\|actionButton.*filetype" R/module-loadpage-ui.R | head -20

Repository: Vitek-Lab/MSstatsShiny

Length of output: 197


Scope the [type=radio][name=...] selectors to the module namespace.

The selectors at lines 858–901 hardcode loadpage-filetype and global [value=...] attributes. If this module is mounted with a different ID or multiple instances are added, these selectors will target the wrong elements or fail entirely.

Use session$ns(NAMESPACE_LOADPAGE$filetype) to construct the correct name attribute for the radio group.

Example fix
+  filetype_name <- session$ns(NAMESPACE_LOADPAGE$filetype)
+  filetype_name_attr <- sprintf("[name='%s']", filetype_name)
+  filetype_selector <- sprintf("[type=radio]%s", filetype_name_attr)
+
   observe({
     if ((input$BIO == "Protein" || input$BIO == "Peptide") && input$DDA_DIA == "LType") {
-      runjs("$('[type=radio][name=loadpage-filetype]:disabled')...")
+      runjs(sprintf("$('%s:disabled').parent().parent().parent().find('div.radio').css('opacity', 1)", filetype_selector))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@R/loadpage-server-rendering.R` around lines 855 - 901, The radio-button
selectors in the observe block are hardcoded to the global loadpage-filetype
name, so they won’t work correctly when the module ID changes or multiple
instances exist. Update the selector logic in this observe() path to use
session$ns with NAMESPACE_LOADPAGE$filetype so the [type=radio][name=...]
lookups are module-scoped, and keep the value-based disable calls tied to that
namespaced radio group.

Comment on lines +78 to +81
onclick("proceed1", {
get_data()
get_annot()
shinyjs::show("summary_tables")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Stop before showing summaries when data loading fails.

get_data() can return NULL after a loader error, but this still shows summary_tables and exposes “Next step” with no valid dataset. Gate on the loaded value first.

Proposed fix
 onclick("proceed1", {
-    get_data()
+    loaded_data <- get_data()
+    req(!is.null(loaded_data))
     get_annot()
     shinyjs::show("summary_tables")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onclick("proceed1", {
get_data()
get_annot()
shinyjs::show("summary_tables")
onclick("proceed1", {
loaded_data <- get_data()
req(!is.null(loaded_data))
get_annot()
shinyjs::show("summary_tables")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@R/loadpage-server-summary.R` around lines 78 - 81, The proceed1 click handler
currently shows summary_tables even when get_data() fails and returns NULL.
Update the proceed1 callback to capture the result of get_data() and only
continue to get_annot() and shinyjs::show("summary_tables") when a valid dataset
is loaded; otherwise stop early and keep the Next step hidden. Use the proceed1
onclick handler and get_data() as the key points to locate the fix.

Comment on lines +85 to +97
if (!is.null(app_template) && app_template() == TEMPLATES$protein_turnover) {
tryCatch({
data <- get_data()
if (!is.null(data) && "Condition" %in% colnames(data)) {
conditions <- unique(as.character(data$Condition))
time_vals <- as.character(autofill_condition_value(conditions))
time_vals[is.na(time_vals) | time_vals == "NA"] <- "?"
meta_df <- data.frame(Condition = conditions,
TimeVal = time_vals,
stringsAsFactors = FALSE)
condition_metadata(meta_df)
}
}, error = function(e) {})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Surface protein-turnover metadata initialization failures.

The empty error handler silently leaves condition_metadata unset while rendering the turnover UI. Mirror the chemoproteomics warning path.

Proposed fix
-      }, error = function(e) {})
+      }, error = function(e) {
+        condition_metadata(NULL)
+        showNotification(
+          paste("Could not initialize condition metadata:", conditionMessage(e)),
+          type = "warning",
+          duration = 6
+        )
+      })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!is.null(app_template) && app_template() == TEMPLATES$protein_turnover) {
tryCatch({
data <- get_data()
if (!is.null(data) && "Condition" %in% colnames(data)) {
conditions <- unique(as.character(data$Condition))
time_vals <- as.character(autofill_condition_value(conditions))
time_vals[is.na(time_vals) | time_vals == "NA"] <- "?"
meta_df <- data.frame(Condition = conditions,
TimeVal = time_vals,
stringsAsFactors = FALSE)
condition_metadata(meta_df)
}
}, error = function(e) {})
if (!is.null(app_template) && app_template() == TEMPLATES$protein_turnover) {
tryCatch({
data <- get_data()
if (!is.null(data) && "Condition" %in% colnames(data)) {
conditions <- unique(as.character(data$Condition))
time_vals <- as.character(autofill_condition_value(conditions))
time_vals[is.na(time_vals) | time_vals == "NA"] <- "?"
meta_df <- data.frame(Condition = conditions,
TimeVal = time_vals,
stringsAsFactors = FALSE)
condition_metadata(meta_df)
}
}, error = function(e) {
condition_metadata(NULL)
showNotification(
paste("Could not initialize condition metadata:", conditionMessage(e)),
type = "warning",
duration = 6
)
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@R/loadpage-server-summary.R` around lines 85 - 97, Surface failures in the
protein_turnover metadata setup instead of swallowing them in the tryCatch
around get_data and condition_metadata. In the app_template() ==
TEMPLATES$protein_turnover branch, update the error handler to mirror the
chemoproteomics path by logging a warning or message with the caught error e, so
loadpage-server-summary.R makes initialization issues visible while still
allowing the UI to render.

Comment thread R/module-loadpage-server.R Outdated
@swaraj-neu

Copy link
Copy Markdown
Contributor Author
  • Fixed the helper-count docs (5 -> 6) which was a real miss from this PR.
  • The other five are pre-existing logic that the file split moved out of module-loadpage-server.R; none were introduced or changed by this PR, which is intentional since it's a behavior-preserving refactor

Comment thread R/constants.R Outdated
)

NAMESPACE_LOADPAGE = list(
# Cross-module public IDs (read from outside the loadpage module).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you remove the comments for the constants, they don't seem to add anything to understanding these constants.

Comment thread R/loadpage-server-data-loaders.R Outdated
Comment on lines +5 to +16
# Extracted from R/module-loadpage-server.R by the Phase 2 server split.
# Pure cut-and-paste: no behavior change, no reactivity timing change, no
# input-ID renames. Owns:
# - 11 single-file wrapper reactives (`get_annot`, `get_annot1/2/3`,
# `get_evidence`, `get_evidence2`, `get_global`, `get_proteinGroups`,
# `get_proteinGroups2`, `get_FragSummary`, `get_peptideSummary`,
# `get_protSummary`, `get_maxq_ptm_sites`)
# - the lynchpin `get_data` eventReactive (triggered on `proceed1`)
# - the download_msstats_format downloadHandler + its enable/disable
# observers
# - `get_data_code` (triggered on `calculate`)
# - `get_summary1`, `get_summary2` (triggered on `proceed1`)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you get rid of these comments and similar comments? Seems to be written in a way where you need context from the previous commit

Comment thread R/loadpage-server-data-loaders.R Outdated
@@ -0,0 +1,177 @@
# ============================================================================
# Loadpage — data-loading reactives + download MSstats handler + summaries

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you elaborate that the summaries are summary experimental statistics, e.g. number of conditions, number of replicates etc?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@tonywu1999 Done. Clarified that these are the experimental-design summary statistics (number of conditions, number of replicates, etc.). Since they're moving to loadpage-server-summary.R per your other comment, the clarified description now lives there.

Comment thread R/loadpage-server-data-loaders.R Outdated
Comment on lines +150 to +156
get_summary1 <- eventReactive(input$proceed1, {
getSummary1(input, get_data(), get_annot())
})

get_summary2 <- eventReactive(input$proceed1, {
getSummary2(input, get_data())
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should this be in loadpage-server-summary.R?

Comment on lines +133 to +151
output$template <- downloadHandler(
filename = "extdata/templateannotation.csv",

content = function(file) {
file.copy("extdata/templateannotation.csv", file)
},
contentType = "csv"
)

output$template1 <- downloadHandler(
filename = function() {
paste("extdata/templateevidence", "txt", sep = ".")
},

content = function(file) {
file.copy("extdata/templateevidence.txt", file)
},
contentType = "txt"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you remind me where we see the template downloads in the loadpage tab? I don't recall being able to download anything like this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@tonywu1999 you're right, there's no way to download these in the loadpage tab, they're dead handlers that predate this PR, want me to remove them?

Comment thread R/loadpage-server-rendering.R Outdated
@@ -0,0 +1,907 @@
# ============================================================================

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we rename this file to loadpage-server-converter-options-panel.R?

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
R/loadpage-server-converter-options-panel.R (2)

60-62: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Include the LabelFreeType picker’s ancestor gates in sample descriptions.

sample_*_description_panel divs are mounted separately from label_free_type_selection_panel, so stale LabelFreeType can keep a sample description visible after switching to TMT or PTM. Mirror loadpage_show_label_free_type_selection() by also checking bio and dda_dia.

Proposed fix
-loadpage_show_sample_dataset_description <- function(filetype, label_free_type, mode) {
-  isTRUE(filetype == "sample") && isTRUE(label_free_type == mode)
+loadpage_show_sample_dataset_description <- function(bio, filetype, dda_dia,
+                                                     label_free_type, mode) {
+  loadpage_show_label_free_type_selection(bio, filetype, dda_dia) &&
+    isTRUE(label_free_type == mode)
 }
       condition = loadpage_show_sample_dataset_description(
+        input[[NAMESPACE_LOADPAGE$bio]],
         input[[NAMESPACE_LOADPAGE$filetype]],
+        input[[NAMESPACE_LOADPAGE$dda_dia]],
         input[[NAMESPACE_LOADPAGE$label_free_type]],
         "DDA"
       )

Apply the same argument additions to the DIA and SRM/PRM calls.

Also applies to: 366-395

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@R/loadpage-server-converter-options-panel.R` around lines 60 - 62, The sample
description visibility logic in loadpage_show_sample_dataset_description only
checks filetype and label_free_type, so stale LabelFreeType state can leave
sample panels visible after switching modes. Update this helper to mirror
loadpage_show_label_free_type_selection by also requiring the bio and dda_dia
ancestor gates, and then propagate the same extra arguments through the DIA and
SRM/PRM call sites so sample_*_description_panel visibility stays in sync with
label_free_type_selection_panel.

804-851: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reset input$filetype when a selected converter becomes disabled. The observer only disables and dims radios; if the user had already picked one, input$filetype can stay on that invalid value and keep driving the loadpage_show_* / getData() branches. Clear it or switch to a valid fallback when the active option is disabled.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@R/loadpage-server-converter-options-panel.R` around lines 804 - 851, The
observer in loadpage-server-converter-options-panel.R only disables and dims
filetype radios, but it does not clear an already-selected now-invalid value, so
input$filetype can still drive loadpage_show_* and getData() with a disabled
converter. Update the observe block that handles BIO/DDA_DIA cases to reset
input$filetype or assign a valid fallback whenever the chosen option is among
the disabled values, using the existing filetype enable/disable logic and the
loadpage_show_* branches as the reference points.
🧹 Nitpick comments (1)
R/loadpage-server-data-loaders.R (1)

89-125: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Handle utils::zip() failures explicitly

  • utils::zip() returns an exit status rather than raising an R error, so a non-zero exit can leave an empty/invalid .zip without entering the error handler.
  • The fallback writeLines() path writes plain text into the download payload, which produces a broken .csv/.zip silently. Surface the failure with showNotification() and abort the download instead of writing an error message into the file.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@R/loadpage-server-data-loaders.R` around lines 89 - 125, The MSstats download
handler in download_msstats_format currently relies on tryCatch, but
utils::zip() can fail by returning a non-zero exit status without throwing, and
the error fallback writes text into the download file. Update the content
function to explicitly check the zip result after calling utils::zip() and, on
failure, use showNotification() to report the problem and abort instead of
writing any error payload into file; keep the data.frame path and the per-table
export logic in download_msstats_format unchanged otherwise.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@R/loadpage-server-converter-options-panel.R`:
- Around line 60-62: The sample description visibility logic in
loadpage_show_sample_dataset_description only checks filetype and
label_free_type, so stale LabelFreeType state can leave sample panels visible
after switching modes. Update this helper to mirror
loadpage_show_label_free_type_selection by also requiring the bio and dda_dia
ancestor gates, and then propagate the same extra arguments through the DIA and
SRM/PRM call sites so sample_*_description_panel visibility stays in sync with
label_free_type_selection_panel.
- Around line 804-851: The observer in loadpage-server-converter-options-panel.R
only disables and dims filetype radios, but it does not clear an
already-selected now-invalid value, so input$filetype can still drive
loadpage_show_* and getData() with a disabled converter. Update the observe
block that handles BIO/DDA_DIA cases to reset input$filetype or assign a valid
fallback whenever the chosen option is among the disabled values, using the
existing filetype enable/disable logic and the loadpage_show_* branches as the
reference points.

---

Nitpick comments:
In `@R/loadpage-server-data-loaders.R`:
- Around line 89-125: The MSstats download handler in download_msstats_format
currently relies on tryCatch, but utils::zip() can fail by returning a non-zero
exit status without throwing, and the error fallback writes text into the
download file. Update the content function to explicitly check the zip result
after calling utils::zip() and, on failure, use showNotification() to report the
problem and abort instead of writing any error payload into file; keep the
data.frame path and the per-table export logic in download_msstats_format
unchanged otherwise.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2343e99c-9eda-42a0-92eb-345e68c34f05

📥 Commits

Reviewing files that changed from the base of the PR and between 6c8f9b4 and 9d43d9f.

📒 Files selected for processing (8)
  • R/constants.R
  • R/loadpage-server-converter-options-panel.R
  • R/loadpage-server-data-loaders.R
  • R/loadpage-server-preview.R
  • R/loadpage-server-proceed-validation.R
  • R/loadpage-server-summary.R
  • R/module-loadpage-ui.R
  • tests/testthat/test-module-loadpage-ui.R
💤 Files with no reviewable changes (1)
  • R/constants.R
🚧 Files skipped from review as they are similar to previous changes (5)
  • R/loadpage-server-proceed-validation.R
  • R/loadpage-server-preview.R
  • tests/testthat/test-module-loadpage-ui.R
  • R/loadpage-server-summary.R
  • R/module-loadpage-ui.R

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.

2 participants