Skip to content

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

Merged
tonywu1999 merged 9 commits into
develfrom
MSstatsShiny/work/20260619_loadpage_refactor
Jul 2, 2026
Merged

Refactor loadpage: move converter panels to server-side show/hide#217
tonywu1999 merged 9 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

The loadpage workflow was refactored as a behavior-preserving cleanup to improve maintainability. Client-side conditionalPanel logic (and related inline conditions) was replaced with server-side, deterministic visibility predicates that drive show/hide via shinyjs::toggle(). The large loadpage server module was split into focused helper components, including migrating converter-related/anomaly-score panels to server-side renderUI where applicable. During review, a helper-count documentation mismatch (5 → 6) was corrected.

Changes

  • Centralized namespaced control identifiers

    • Added NAMESPACE_LOADPAGE in R/constants.R to define the UI/server “loadpage” control identifiers used for namespacing and server-driven toggling.
  • Loadpage server split into an orchestrator + helper registrations

    • Updated loadpageServer() in R/module-loadpage-server.R to:
      • Remain the orchestrator after the Phase 2 split.
      • Keep only required module state (e.g., editable condition_metadata) and shinyFiles-based local big-file path wiring for non-web servers.
      • Register helper modules in order:
        • register_loadpage_preview()
        • register_loadpage_visibility_observers()
        • register_loadpage_converter_ui()
        • register_loadpage_proceed_validation()
        • register_loadpage_data_loaders()
        • register_loadpage_summary()
      • Return a reduced interface: list(input=..., getData=..., getConditionMetadata=...).
      • Remove the previous in-file preview/conditional logic and local filename renderPrints.
  • Server-side preview, including DIANN/PTM detection and UI wiring

    • Added register_loadpage_preview() in R/loadpage-server-preview.R to:
      • Load a first-100-row preview for supported DIANN and Metamorpheus PTM combinations.
      • Auto-detect whether a DIANN upload is “2+” and synchronize diann_2plus, including mismatch warnings.
      • Render PTM modification-ID selection UI from preview content, with support for a custom “other” path.
  • Server-side proceed enablement/validation

    • Added register_loadpage_proceed_validation() in R/loadpage-server-proceed-validation.R to cascade proceed1 enable/disable based on:
      • input$BIO, input$DDA_DIA, and input$filetype
      • Required uploaded/reactive inputs per selected pathway (including big-file path length checks where relevant).
  • Event-driven data loading and download/export

    • Added register_loadpage_data_loaders() in R/loadpage-server-data-loaders.R to:
      • Provide event-triggered get_data() and get_data_code derived from input$calculate.
      • Implement output$download_msstats_format to export either:
        • a single CSV when the result is a data.frame, or
        • a ZIP of per-table CSVs while skipping NULL/empty tables and erroring if no non-empty tables exist.
      • Remove get_summary1/get_summary2 from the returned loader object.
  • Post-proceed summary UI + editable condition metadata

    • Added register_loadpage_summary() in R/loadpage-server-summary.R to:
      • Create get_summary1/get_summary2 as eventReactive computations driven by proceed1.
      • Render an editable DT for condition_metadata and write edits back into a reactiveVal.
      • Initialize template-specific condition metadata from uploaded data$Condition (including protein_turnover and chemoproteomics behaviors, with warning handling on chemoproteomics initialization failure).
      • Show/hide the correct summary table section(s) and advance to the DataProcessing tab on proceed2.
  • Converter/anomaly UI moved to server-controlled show/hide + renderUI

    • Added R/loadpage-server-converter-options-panel.R providing:
      • A suite of pure loadpage_show_* predicate functions controlling visibility for DIANN/LabelFree/OpenSWATH/quantification/annotation/PTM/TMT sections.
      • register_loadpage_visibility_observers() that drives panel show/hide via shinyjs::toggle() (including BIO-driven toggles for PTM vs non-PTM summary containers after proceed1).
      • register_loadpage_converter_ui() implementing renderUI() outputs for converter fragments (including anomaly-score-related UI via renderUI slots) and enabling/disabling/dimming incompatible converter radio choices based on (BIO, DDA_DIA).
      • TMT which.proteinid handling unified under a single renderUI slot tmt_options_ui, with preservation/seed logic for switching between PD ↔ MaxQuant (loadpage_seed_proteinid()), tracked with reactiveVal.
  • UI refactor: from conditionalPanel to server-driven hidden containers

    • Updated loadpageUI() / UI builders in R/module-loadpage-ui.R to:
      • Replace conditionalPanel-based logic with server-mounted hidden containers (shinyjs::hidden(...)) controlled by the server.
      • Migrate major upload/description/panel clusters (including label-free and most PTM/converter uploads) into hidden div containers.
      • Render TMT options via uiOutput(ns(NAMESPACE_LOADPAGE$tmt_options_ui)).
      • Ensure anomaly UIs use server-driven renderUI slots (e.g., Spectronaut anomaly UI), removing reliance on static conditional-panel/JS condition strings.

Tests

  • Added tests/testthat/test-loadpage-server-rendering.R

    • Truth-table style coverage for loadpage visibility predicate semantics across converter/filetype/LType combinations and key input presence/NULL cases.
    • Phase 1 and Phase 2 assertions for:
      • DIANN LF/options gating, DIANN 2+ gating, q-value/q-cutoff/Mbr/anomaly run-order gating.
      • Sample/label-free selectors and upload-panel visibility across multiple converters and PTM vs non-PTM paths.
      • OpenSWATH m-score cutoff regression requiring the correct ancestor-chain condition.
      • TMT options gating and NAMESPACE_LOADPAGE driver/container id assertions.
    • Added tests for:
      • loadpage_default_proteinid_for_filetype() defaults.
      • loadpage_seed_proteinid() “typed vs default” preservation across PD ↔ MaxQuant switching (including preserved/outgoing edge cases and session reset behavior).
    • Verified post-proceed1 summary-table predicate behavior for PTM vs non-PTM.
  • Updated tests/testthat/test-module-loadpage-ui.R

    • Adjusted expectations to match the new server-driven hidden-container + renderUI architecture.
    • Confirmed:
      • tmt_options_ui is provided via uiOutput(ns("tmt_options_ui")) and the previous static input is absent.
      • Spectronaut and DIANN anomaly UIs are mounted via server renderUI slots and that old static conditionalPanel/JS condition strings are no longer present.
      • Large/regular path anomaly gating is expressed via renderUI-time presence of the run-order fileInput.

Coding guidelines violated

  • No coding guideline violations were identified in the provided change summary.

- 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

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The loadpage module is split into helper registrations for preview, visibility, proceed gating, data loading, and summary handling. UI visibility moves to shared namespaced IDs and server-driven toggles, with new tests covering the gating predicates and rendered containers.

Changes

Loadpage Phase 2 split

Layer / File(s) Summary
Namespace constants and visibility predicates
R/constants.R, R/loadpage-server-converter-options-panel.R, tests/testthat/test-loadpage-server-rendering.R
NAMESPACE_LOADPAGE adds shared loadpage slot IDs, pure loadpage_show_* predicates define panel visibility, loadpage_seed_proteinid preserves PD/MaxQuant values, and tests pin the predicate and seed behavior.
Hidden UI mounting and converter slots
R/module-loadpage-ui.R, R/loadpage-server-converter-options-panel.R, tests/testthat/test-module-loadpage-ui.R
Sample descriptions, uploads, PTM panels, DIANN/OpenSWATH controls, and TMT options move to hidden containers or renderUI slots; visibility observers and converter UI helpers toggle those panels and the UI tests check the new hidden mounting.
Preview, detection, and proceed gating
R/loadpage-server-preview.R, R/loadpage-server-proceed-validation.R
Preview loading, DIANN format detection, Metamorpheus mod-ID rendering, and proceed1 enablement are wired into separate registration helpers.
Data loading and download handler
R/loadpage-server-data-loaders.R
Per-file reactives, get_data, get_data_code, and the MSstats CSV/ZIP download handler are registered here.
Summary cluster and orchestration
R/loadpage-server-summary.R, R/module-loadpage-server.R
Condition metadata editing, summary tables, proceed2 navigation, and the reduced loadpageServer() orchestration are updated together.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: Review effort 3/5

Suggested reviewers: tonywu1999

Poem

A rabbit hops through panels bright,
With hidden IDs and toggles light.
I sniffed the previews, neat and keen,
Then bounded past the proceed screen.

🚥 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 accurately summarizes 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 thread R/loadpage-server-converter-options-panel.R
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
Comment thread R/loadpage-server-data-loaders.R Outdated
Comment thread R/loadpage-server-data-loaders.R Outdated
Comment thread R/loadpage-server-data-loaders.R Outdated
Comment thread R/loadpage-server-summary.R Outdated
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?

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.

@swaraj-neu Yes, you can remove them.

Comment thread R/loadpage-server-rendering.R Outdated

@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

Comment thread R/loadpage-server-converter-options-panel.R Outdated
Comment thread R/loadpage-server-converter-options-panel.R Outdated
Comment thread R/loadpage-server-converter-options-panel.R Outdated
Comment thread R/loadpage-server-converter-options-panel.R Outdated
Comment thread R/loadpage-server-converter-options-panel.R
Comment thread R/loadpage-server-converter-options-panel.R
Comment thread R/loadpage-server-summary.R Outdated
Comment on lines +206 to +215
conditionalPanel(condition = "input['loadpage-BIO'] !== 'PTM'",
h4("Top 6 rows of the dataset"),
div(style = "overflow-x: auto;", tableOutput(ns("summary")))
),
conditionalPanel(condition = "input['loadpage-BIO'] == 'PTM'",
h4("Top 6 rows of the PTM dataset"),
div(style = "overflow-x: auto;", tableOutput(ns("summary_ptm"))),
tags$br(),
h4("Top 6 rows of the unmodified protein dataset"),
div(style = "overflow-x: auto;", tableOutput(ns("summary_prot")))

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.

There's still conditional panels here. Could you adjust?

Comment thread R/module-loadpage-ui.R Outdated
Comment on lines +255 to +256
# PTM MSstats format. (The original JS condition had a redundant TMT
# clause that collapsed to `BIO=='PTM'`; the server predicate folds it.)

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.

You can remove this comment

Comment thread R/module-loadpage-ui.R Outdated
Comment on lines 858 to 879

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.

For both DIANN and Spectronaut, I'm pretty sure you can migrate away from the conditional panel logic here. I would do renderUI - it doesn't matter that an uploaded file needs to be reuploaded if a user switches from DIANN to Spectronaut or another converter. The run order file is really small and we can get away with reuploading it.

@swaraj-neu swaraj-neu requested a review from tonywu1999 July 1, 2026 01:03

@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 (1)
R/loadpage-server-converter-options-panel.R (1)

53-55: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Include the LabelFreeType selector’s ancestor gate in sample descriptions.

The description panels are standalone hidden divs, so a stale LabelFreeType selection can still show DDA/DIA/SRM text after switching away from the label-free sample workflow. Reuse the selector gate so descriptions hide whenever BIO / DDA_DIA hides the picker.

Proposed fix
-loadpage_show_sample_dataset_description <- function(filetype, label_free_type, label_free_mode) {
-  isTRUE(filetype == "sample") && isTRUE(label_free_type == label_free_mode)
+loadpage_show_sample_dataset_description <- function(bio, filetype, dda_dia,
+                                                     label_free_type,
+                                                     label_free_mode) {
+  loadpage_show_sample_dataset_label_free_type_selector(bio, filetype, dda_dia) &&
+    isTRUE(label_free_type == label_free_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 update to the DIA and SRM/PRM observers.

Also applies to: 353-381

🤖 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 53 - 55, The sample
description visibility logic in loadpage_show_sample_dataset_description only
checks filetype and label_free_type, so it can keep showing stale DDA/DIA/SRM
text after the LabelFreeType picker is hidden. Update
loadpage_show_sample_dataset_description to also take the selector’s ancestor
gate used for BIO/DDA_DIA visibility, and require that gate to be true before
showing any description. Then apply the same argument update in the DIA and
SRM/PRM observers so all description panels use the shared gate consistently.
🤖 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 53-55: The sample description visibility logic in
loadpage_show_sample_dataset_description only checks filetype and
label_free_type, so it can keep showing stale DDA/DIA/SRM text after the
LabelFreeType picker is hidden. Update loadpage_show_sample_dataset_description
to also take the selector’s ancestor gate used for BIO/DDA_DIA visibility, and
require that gate to be true before showing any description. Then apply the same
argument update in the DIA and SRM/PRM observers so all description panels use
the shared gate consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ab223b5b-682b-4472-aaec-a277a7ff244b

📥 Commits

Reviewing files that changed from the base of the PR and between 99c9a99 and 5f05962.

📒 Files selected for processing (6)
  • R/constants.R
  • R/loadpage-server-converter-options-panel.R
  • R/loadpage-server-summary.R
  • R/module-loadpage-ui.R
  • tests/testthat/test-loadpage-server-rendering.R
  • tests/testthat/test-module-loadpage-ui.R
💤 Files with no reviewable changes (3)
  • R/constants.R
  • tests/testthat/test-loadpage-server-rendering.R
  • R/loadpage-server-summary.R

- Change loadpage_show_sample_dataset_description
- Update the three call sites blocks
- Update the truth-table tests in test-loadpage-server-rendering.R
@tonywu1999 tonywu1999 merged commit 9eec81f into devel Jul 2, 2026
2 checks passed
@tonywu1999 tonywu1999 deleted the MSstatsShiny/work/20260619_loadpage_refactor branch July 2, 2026 14:31
@tonywu1999 tonywu1999 restored the MSstatsShiny/work/20260619_loadpage_refactor branch July 2, 2026 14:31
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