Skip to content

feature(diann): Enable support for DIANN 2.0 and parquet files#114

Merged
tonywu1999 merged 6 commits intodevelfrom
diann-2
Sep 5, 2025
Merged

feature(diann): Enable support for DIANN 2.0 and parquet files#114
tonywu1999 merged 6 commits intodevelfrom
diann-2

Conversation

@tonywu1999
Copy link
Copy Markdown
Contributor

@tonywu1999 tonywu1999 commented Aug 27, 2025

PR Type

Enhancement, Tests, Documentation


Description

  • Add DIANN 2.0 and parquet support

  • Refactor loadpage UI into subcomponents

  • Introduce DIANN-specific UI options

  • Add tests for loadpage UI structure


Diagram Walkthrough

flowchart LR
  UI["Loadpage UI refactored into helpers"]
  DIANN["DIANN import: parquet + v2.0 options"]
  ArrowDep["Add arrow dependency and import"]
  Docs["Roxygen keywords and Rd updates"]
  Tests["New testthat for loadpage UI"]

  UI -- "modular helpers" --> Tests
  DIANN -- "read_parquet + quant column" --> ArrowDep
  ArrowDep -- "DESCRIPTION/NAMESPACE" --> Docs
Loading

File Walkthrough

Relevant files
Dependencies
2 files
DESCRIPTION
Add arrow to Imports for parquet support                                 
+1/-1     
NAMESPACE
Import arrow::read_parquet symbol                                               
+1/-0     
Documentation
3 files
MSstatsShiny.R
Package doc roxygen updated to internal                                   
+2/-2     
MSstatsShiny.Rd
Mark package docs as internal                                                       
+1/-0     
loadpageUI.Rd
Adjust description punctuation                                                     
+1/-1     
Enhancement
2 files
module-loadpage-ui.R
Modularize loadpage UI and add DIANN options                         
+521/-282
utils.R
Support DIANN parquet and v2.0 quant column                           
+27/-9   
Tests
1 files
test-module-loadpage-ui.R
Add tests for loadpage UI structure and IDs                           
+109/-0 

Summary by CodeRabbit

  • New Features

    • DIANN uploads now accept Parquet (.parquet, .pq) files.
    • DIANN quantification can be automatic or use a user-selected intensity column.
  • Refactor

    • Load page UI reorganized into modular internal components; public APIs and user workflows unchanged.
  • Documentation

    • Package documentation marked internal; loadpageUI description wording clarified.
  • Tests

    • Extensive UI tests added for loadpageUI and its helpers.
  • Chores

    • Added runtime support for Parquet and file-extension detection.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 27, 2025

Warning

Rate limit exceeded

@tonywu1999 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 14 minutes and 10 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 28a59f1 and 54d835c.

📒 Files selected for processing (8)
  • DESCRIPTION (1 hunks)
  • NAMESPACE (2 hunks)
  • R/MSstatsShiny.R (1 hunks)
  • R/module-loadpage-ui.R (2 hunks)
  • R/utils.R (3 hunks)
  • man/MSstatsShiny.Rd (1 hunks)
  • man/loadpageUI.Rd (1 hunks)
  • tests/testthat/test-module-loadpage-ui.R (1 hunks)

Walkthrough

Adds arrow and tools imports; converts package roxygen to internal package style; modularizes loadpageUI into internal create_* helpers (public signature unchanged); extends DIANN handling to accept Parquet and a quantificationColumn; updates docs and adds comprehensive UI tests.

Changes

Cohort / File(s) Summary
Package metadata
DESCRIPTION, NAMESPACE
DESCRIPTION: add arrow and tools to Imports and expand shinydashboard. NAMESPACE: add importFrom(arrow, read_parquet) and importFrom(tools, file_ext).
Package docs/internalization
R/MSstatsShiny.R, man/MSstatsShiny.Rd
Convert package-level roxygen to internal style: remove @docType package/@name/NULL, add #' @keywords internal and "_PACKAGE"; add \keyword{internal} to Rd.
Loadpage UI modularization and docs/tests
R/module-loadpage-ui.R, man/loadpageUI.Rd, tests/testthat/test-module-loadpage-ui.R
Refactor loadpageUI internals into many create_* helpers (header, samples, CSS, main selections, label-free selection, per-format upload blocks, separators, PTM helpers). Public loadpageUI(id) unchanged. Fix man/loadpageUI.Rd wording. Add extensive UI tests covering structure, namespaced IDs, options, conditionals, file inputs, separators, tooltips, and upload constraints.
Data utils: DIANN + Parquet
R/utils.R
Add getFileExtension(filename) helper and roxygen import directives for tools::file_ext and arrow::read_parquet. In getData, DIANN branch recognizes .parquet/.pq and uses read_parquet; otherwise reads CSV. Add quantificationColumn logic ("auto" or input$intensity_column) and pass it to DIANNtoMSstatsFormat. Remove stray print.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as loadpageUI
  participant Server as getData()
  participant Arrow as arrow::read_parquet
  participant CSV as read.csv
  participant DIANN as DIANNtoMSstatsFormat

  User->>UI: choose DIANN file + options
  UI->>Server: submit file path + diann_2plus + intensity_column
  Server->>Server: ext = getFileExtension(path)
  alt ext ∈ {.parquet, .pq}
    Server->>Arrow: read_parquet(path)
    Arrow-->>Server: table
  else
    Server->>CSV: read.csv(path, ...)
    CSV-->>Server: table
  end
  Server->>Server: set quantificationColumn ("auto" or intensity_column)
  Server->>DIANN: DIANNtoMSstatsFormat(table, quantificationColumn=...)
  DIANN-->>Server: MSstats-format data
  Server-->>UI: data ready
  UI-->>User: proceed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I thump on parquet, arrows bright,
Helpers stitch the upload light.
UI hops into tidy rooms,
Tests nibble corners, squash the glooms.
A carrot for each passing test—🥕🐇

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch diann-2

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link
Copy Markdown

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Issue

In conditionalPanel conditions, JavaScript uses !== in some places; Shiny conditionalPanel evaluates R-like JS via jQuery; using !== may fail in some Shiny contexts. Also comparisons to string literals are inconsistent across panels. Validate that "!== 'sample'" works as intended across browsers and Shiny versions.

conditionalPanel(
  condition = "input['loadpage-filetype'] && input['loadpage-DDA_DIA'] == 'LType' && input['loadpage-filetype'] !== 'sample' && input['loadpage-filetype'] !== 'MRF'",
  h4("Select the options for pre-processing"),
  checkboxInput(ns("uniqe_peptides"), "Use unique peptides", value = TRUE),
  checkboxInput(ns("remove"), "Remove proteins with 1 peptide and charge", value = FALSE)
),

conditionalPanel(
  condition = "input['loadpage-filetype'] && input['loadpage-DDA_DIA'] == 'LType' && input['loadpage-filetype'] !== 'sample'",
  checkboxInput(ns("remove"), "Remove proteins with 1 feature", value = FALSE),

  # Quality filtering options
  create_quality_filtering_options(ns)
Robustness

Reading DIANN parquet via arrow::read_parquet returns a Arrow Table/RecordBatchReader or tibble depending on version; ensure downstream DIANNtoMSstatsFormat accepts this. Consider converting to data.frame explicitly and handling missing packages or extension mismatch gracefully.

  mydata = SpectronauttoMSstatsFormat(data,
                                      annotation = getAnnot(input),
                                      filter_with_Qvalue = TRUE, ## same as default
                                      qvalue_cutoff = 0.01, ## same as default
                                      removeProtein_with1Feature = TRUE,
                                      use_log_file = FALSE)
}
else if(input$filetype == 'diann') {
  if (getFileExtension(input$dianndata$name) %in% c("parquet", "pq")) {
    data = read_parquet(input$dianndata$datapath)
  } else {
    data = read.csv(input$dianndata$datapath, sep=input$sep_dianndata)
  }

  qvalue_cutoff = 0.01
  MBR = FALSE
  if (input$q_val) {
    qvalue_cutoff = if (!is.null(input$q_cutoff) && input$q_cutoff >= 0 && input$q_cutoff <= 1) input$q_cutoff else 0.01
    MBR = if (!is.null(input$MBR)) input$MBR else FALSE
  }
  quantificationColumn = "auto"
  if (!input$diann_2plus) {
    quantificationColumn = input$intensity_column
  }

  mydata = DIANNtoMSstatsFormat(data,
                                annotation = getAnnot(input),
                                qvalue_cutoff = qvalue_cutoff,
                                MBR = MBR,
                                removeProtein_with1Feature = TRUE,
                                removeFewMeasurements = FALSE,
                                use_log_file = FALSE,
                                quantificationColumn = quantificationColumn
  )
Accessibility

Many h4/h5 labels include icons and tooltips but some inputs (e.g., fileInput) lack accessible labels (empty label strings). Consider providing descriptive labels or aria-labels to improve accessibility and screen reader support.

create_standard_uploads <- function(ns) {
  conditionalPanel(
    condition = "(input['loadpage-filetype'] =='10col' || input['loadpage-filetype'] =='prog' || input['loadpage-filetype'] =='PD' || input['loadpage-filetype'] =='open'||
                   input['loadpage-filetype'] =='openms' || input['loadpage-filetype'] =='spmin' || input['loadpage-filetype'] == 'phil') && input['loadpage-BIO'] != 'PTM'",
    h4("4. Upload quantification dataset"),
    fileInput(ns('data'), "", multiple = FALSE, accept = NULL),
    create_separator_buttons(ns, "sep_data")
  )
}

#' Create standard annotation file uploads
#' @noRd
create_standard_annotation_uploads <- function(ns) {
  conditionalPanel(
    condition = "(input['loadpage-filetype'] == 'sky' || input['loadpage-filetype'] == 'prog' || input['loadpage-filetype'] == 'PD' || input['loadpage-filetype'] == 'spec' || input['loadpage-filetype'] == 'open'|| input['loadpage-filetype'] =='spmin' || input['loadpage-filetype'] == 'phil' || input['loadpage-filetype'] == 'diann') && input['loadpage-BIO'] != 'PTM'",
    h4("5. Upload annotation File", class = "icon-wrapper", 
       icon("question-circle", lib = "font-awesome"),
       div("Upload manually created annotation file. This file maps MS runs to experiment metadata (i.e. conditions, bioreplicates). Please see Help tab for information on creating this file.", class = "icon-tooltip")),
    fileInput(ns('annot'), "", multiple = FALSE, accept = c(".csv"))
  )
}

#' Create MSstats format file uploads
#' @noRd
create_msstats_uploads <- function(ns) {
  tagList(
    # Regular MSstats format
    conditionalPanel(
      condition = "input['loadpage-filetype'] == 'msstats' && (input['loadpage-BIO'] != 'PTM' && (input['loadpage-BIO'] != 'PTM' && input['loadpage-DDA_DIA'] != 'TMT'))",
      h4("4. Upload data in MSstats Format"),
      fileInput(ns('msstatsdata'), "", multiple = FALSE, accept = NULL),
      create_separator_buttons(ns, "sep_msstatsdata")
    ),

    # PTM MSstats format
    conditionalPanel(
      condition = "input['loadpage-filetype'] == 'msstats' && (input['loadpage-BIO'] == 'PTM' || (input['loadpage-BIO'] == 'PTM' && input['loadpage-DDA_DIA'] == 'TMT'))",
      h4("4. Upload PTM data in MSstats Format"),
      fileInput(ns('msstatsptmdata'), "", multiple = FALSE, accept = NULL),
      create_separator_buttons(ns, "sep_msstatsptmdata"),

      h4("5. (Optional) Upload unmodified data in MSstats Format"),
      fileInput(ns('unmod'), "", multiple = FALSE, accept = NULL),
      tags$br()
    )
  )
}

#' Create Skyline file uploads
#' @noRd
create_skyline_uploads <- function(ns) {
  conditionalPanel(
    condition = "input['loadpage-filetype'] == 'sky' && input['loadpage-BIO'] != 'PTM'",
    h4("4. Upload MSstats report from Skyline"),
    fileInput(ns('skylinedata'), "", multiple = FALSE, accept = NULL),
    create_separator_buttons(ns, "sep_skylinedata")
  )
}

#' Create DIANN file uploads
#' @noRd
create_diann_uploads <- function(ns) {
  conditionalPanel(
    condition = "input['loadpage-filetype'] == 'diann' && input['loadpage-BIO'] != 'PTM'",
    h4("4. Upload MSstats report from DIANN"),
    fileInput(ns('dianndata'), "", multiple = FALSE, accept = NULL),
    create_separator_buttons(ns, "sep_dianndata")
  )
}

#' Create Spectronaut file uploads
#' @noRd
create_spectronaut_uploads <- function(ns) {
  conditionalPanel(
    condition = "input['loadpage-filetype'] == 'spec' && input['loadpage-BIO'] != 'PTM'",
    h4("4. Upload MSstats scheme output from Spectronaut"),
    fileInput(ns('specdata'), "", multiple = FALSE, accept = NULL),
    create_separator_buttons(ns, "sep_specdata")
  )
}

#' Create PTM FragPipe uploads
#' @noRd
create_ptm_fragpipe_uploads <- function(ns) {
  conditionalPanel(
    condition = "input['loadpage-filetype'] == 'phil' && input['loadpage-BIO'] == 'PTM'",
    h4("4. Upload PTM msstats dataset"),
    fileInput(ns('ptmdata'), "", multiple = FALSE, accept = NULL),

    h4("5. Upload PTM annotation file"),
    fileInput(ns('annotation'), "", multiple = FALSE, accept = c(".csv")),

    h4("6. Upload global profling msstats dataset (optional)"),
    fileInput(ns('globaldata'), "", multiple = FALSE, accept = NULL),

    h4("7. Upload global profling annotation file (optional)"),
    fileInput(ns('globalannotation'), "", multiple = FALSE, accept = c(".csv")),

    h4("Select the options for pre-processing"),
    textInput(ns("mod_id_col"), 
              h5("Please enter the name of the modification id column", class = "icon-wrapper", 
                 icon("question-circle", lib = "font-awesome"),
                 div("Only part of the string is required. For example if your mod id column is named 'STY:1221.12' you only need to enter 'STY' here.", class = "icon-tooltip")),
              value = "STY"),

    textInput(ns("localization_cutoff"), 
              h5("Please enter the localization_cutoff", class = "icon-wrapper", 
                 icon("question-circle", lib = "font-awesome"),
                 div("The probability cutoff used to determine if a modification should be marked or not. If a site cannot be localized it may be dropped depending on the option below.", class = "icon-tooltip")),
              value = ".75"),

    radioButtons(ns("remove_unlocalized_peptides"), 
                 h5("Remove unlocalized peptides", class = "icon-wrapper", 
                    icon("question-circle", lib = "font-awesome"),
                    div("Should peptides without all sites localized be kept or removed.", class = "icon-tooltip")),
                 c(Yes=TRUE, No=FALSE), inline=TRUE)
  )
}

#' Create MaxQuant file uploads
#' @noRd
create_maxquant_uploads <- function(ns) {
  conditionalPanel(
    condition = "input['loadpage-filetype'] == 'maxq' && input['loadpage-BIO'] != 'PTM' && (input['loadpage-DDA_DIA'] == 'TMT' || input['loadpage-DDA_DIA'] == 'LType')",
    h4("4. Upload evidence.txt File"),
    fileInput(ns('evidence'), "", multiple = FALSE, accept = NULL),

    h4("5. Upload proteinGroups.txt File"),
    fileInput(ns('pGroup'), "", multiple = FALSE, accept = NULL),

    h4("6. Upload annotation File", class = "icon-wrapper", 
       icon("question-circle", lib = "font-awesome"),
       div("Upload manually created annotation file. This file maps MS runs to experiment metadata (i.e. conditions, bioreplicates). Please see Help tab for information on creating this file.", class = "icon-tooltip")),
    fileInput(ns('annot1'), "", multiple = FALSE, accept = c(".csv"))
  )
}

#' Create PTM file uploads (for MaxQuant, PD, Spectronaut, Skyline)
#' @noRd
create_ptm_uploads <- function(ns) {
  tagList(
    conditionalPanel(
      condition = "(input['loadpage-filetype'] == 'maxq' || input['loadpage-filetype'] == 'PD' || input['loadpage-filetype'] == 'spec' || input['loadpage-filetype'] == 'sky') && (input['loadpage-BIO'] == 'PTM' || (input['loadpage-BIO'] == 'PTM' && input['loadpage-DDA_DIA'] == 'TMT'))",
      h4("4. Upload PTM input.txt File"),
      fileInput(ns('ptm_input'), "", multiple = FALSE, accept = NULL),

      h4("5. Upload annotation File", class = "icon-wrapper", 
         icon("question-circle", lib = "font-awesome"),
         div("Upload manually created annotation file. This file maps MS runs to experiment metadata (i.e. conditions, bioreplicates). Please see Help tab for information on creating this file.", class = "icon-tooltip")),

@github-actions
Copy link
Copy Markdown

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Normalize parquet to data.frame and guard null

read_parquet returns an Arrow Table; convert it to a data.frame/tibble to match
downstream expectations. Also guard against missing input$dianndata to prevent null
dereference when the file isn't provided yet.

R/utils.R [527-531]

+if (is.null(input$dianndata)) {
+  stop("No DIANN file provided.")
+}
 if (getFileExtension(input$dianndata$name) %in% c("parquet", "pq")) {
-  data = read_parquet(input$dianndata$datapath)
+  data <- as.data.frame(read_parquet(input$dianndata$datapath))
 } else {
-  data = read.csv(input$dianndata$datapath, sep=input$sep_dianndata)
+  data <- read.csv(input$dianndata$datapath, sep = input$sep_dianndata)
 }
Suggestion importance[1-10]: 8

__

Why: Adding a null guard avoids runtime errors when no file is provided, and converting Arrow tables to data.frame aligns with downstream expectations. This directly addresses new parquet support and enhances robustness with minimal risk.

Medium
Fix condition operators and input ID typo

JavaScript in Shiny conditionalPanel does not support the strict inequality operator
!==; use != to ensure the condition evaluates correctly. Also correct the typo in
the input ID uniqe_peptides to avoid downstream input lookup issues.

R/module-loadpage-ui.R [485-491]

-conditionalPanel(condition = "input['loadpage-filetype'] && input['loadpage-DDA_DIA'] == 'LType' && input['loadpage-filetype'] !== 'sample' && input['loadpage-filetype'] !== 'MRF'",
+conditionalPanel(condition = "input['loadpage-filetype'] && input['loadpage-DDA_DIA'] == 'LType' && input['loadpage-filetype'] != 'sample' && input['loadpage-filetype'] != 'MRF'",
                  h4("Select the options for pre-processing"),
-                 checkboxInput(ns("uniqe_peptides"), "Use unique peptides", value = TRUE),
+                 checkboxInput(ns("unique_peptides"), "Use unique peptides", value = TRUE),
                  checkboxInput(ns("remove"), "Remove proteins with 1 peptide and charge", value = FALSE))
Suggestion importance[1-10]: 7

__

Why: Replacing !== with != is necessary for Shiny’s JS evaluation, and correcting the misspelled input ID prevents downstream input lookup bugs. Both points are accurate and impactful, though the ID change requires corresponding server-side updates not shown here.

Medium
Fix incorrect panel condition

The condition is missing a specific check for filetype == 'sample', causing this
block to show for any selected file type when label type is DIA. Constrain the
condition to filetype == 'sample' to avoid misleading UI text.

R/module-loadpage-ui.R [93-99]

-conditionalPanel(condition = "input['loadpage-filetype'] && input['loadpage-DDA_DIA'] == 'DIA'",
+conditionalPanel(condition = "input['loadpage-filetype'] == 'sample' && input['loadpage-DDA_DIA'] == 'DIA'",
                  p("The sample dataset for DIA acquisition is taken from the publication ",
                    a("Selevsek, N. et al. Reproducible and Consistent Quantification of the Saccharomyces Cerevisiae Proteome by SWATH-Mass Spectrometry. Molecular & Cellular Proteomics: MCP 14.3 (2015): 739–749. ", 
                      href = "http://www.mcponline.org/content/14/3/739.long", 
                      target="_blank"))
 )
Suggestion importance[1-10]: 6

__

Why: The existing condition for the DIA sample description lacks the explicit filetype == 'sample' check, so tightening it improves UI accuracy without altering logic elsewhere. This is a reasonable, contextually correct refinement but not critical.

Low

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (3)
man/MSstatsShiny.Rd (1)

7-8: Fix spelling: “differencially” → “differentially”

User-facing title/description contain a typo.

Apply this diff:

-\\title{MSstatsShiny: An R-shiny based package for detecting differencially abundant
+\\title{MSstatsShiny: An R-shiny based package for detecting differentially abundant
man/loadpageUI.Rd (1)

12-18: Fix return type and tighten description.

Docs claim "returns nothing," but loadpageUI returns a shiny.tag.list (confirmed by tests). Update value and polish description.

Apply this diff (regenerate via roxygen in R/module-loadpage-ui.R, do not hand-edit Rd):

-\value{
-This function returns nothing, as it sets up the loadpage UI
-}
+\value{
+A \code{shiny.tag.list} that defines the Load page UI.
+}
@@
-\description{
-This function sets up the loadpage UI where it consists of several
-options for users to select and upload files.
-}
+\description{
+Sets up the Load page UI with options for selecting workflows and uploading files.
+}
R/utils.R (1)

788-797: Update code export (getDataCode) to reflect Parquet and quantificationColumn.

The generated “How to” code still assumes CSV-only and lacks quantificationColumn. Align with new DIANN behavior.

-    else if(input$filetype == 'diann') {
-      
-      codes = paste(codes, "data = read.csv(\"insert your MSstats scheme output from DIANN filepath\", header = TRUE, sep = '\\t')\nannot_file = read.csv(\"insert your annotation filepath\")#Optional\n"
-                    , sep = "")
-      
-      codes = paste(codes, "data = DIANNtoMSstatsFormat(data,
-                                       annotation = annot_file, #Optional
-                                       qvalue_cutoff = 0.01, ## same as default
-                                       removeProtein_with1Feature = TRUE,
-                                       use_log_file = FALSE)\n", sep = "")
-    }
+    else if(input$filetype == 'diann') {
+      codes = paste0(codes,
+        "diann_path <- \"insert your DIANN file path (.csv/.tsv/.parquet)\"\n",
+        "annot_file <- read.csv(\"insert your annotation filepath\") # Optional\n",
+        "if (grepl(\"\\\\.(parquet|pq)$\", tolower(diann_path))) {\n",
+        "  library(arrow)\n",
+        "  data <- as.data.frame(arrow::read_parquet(diann_path))\n",
+        "} else {\n",
+        "  data <- read.csv(diann_path, header = TRUE, sep = '\\t')\n",
+        "}\n",
+        "data <- DIANNtoMSstatsFormat(data,\n",
+        "  annotation = annot_file, # Optional\n",
+        "  qvalue_cutoff = 0.01,\n",
+        "  removeProtein_with1Feature = TRUE,\n",
+        "  use_log_file = FALSE,\n",
+        "  quantificationColumn = \"auto\")\n"
+      )
+    }
🧹 Nitpick comments (11)
R/MSstatsShiny.R (1)

47-48: Complete the roxygen “internal package” pattern

You added @keywords internal and "_PACKAGE", but @name MSstatsShiny remains. Prefer the canonical pattern without @name to avoid duplicate/legacy Rd scaffolding.

Apply this diff:

-#' @name MSstatsShiny
 #' @keywords internal
 "_PACKAGE"
R/module-loadpage-ui.R (5)

272-277: Allow Parquet uploads for DIANN

Expose Parquet extensions in the file chooser to match backend support.

Apply this diff:

-    fileInput(ns('dianndata'), "", multiple = FALSE, accept = NULL),
+    fileInput(ns('dianndata'), "", multiple = FALSE, accept = c(".csv", ".tsv", ".txt", ".parquet", ".pq")),

114-117: Stale CSS selector for #loadpage-reset1

No reset1 control is defined here. Either add the control or drop the style to avoid dead code.


212-214: Condition lists an unreachable filetype '10col'

'10col' is not among the filetype choices; keep conditions aligned with available options.

Apply this diff:

-    condition = "(input['loadpage-filetype'] =='10col' || input['loadpage-filetype'] =='prog' || input['loadpage-filetype'] =='PD' || input['loadpage-filetype'] =='open'||
+    condition = "(input['loadpage-filetype'] =='prog' || input['loadpage-filetype'] =='PD' || input['loadpage-filetype'] =='open'||

486-489: Remove legacy MRF guard or add it to choices

'MRF' is not a selectable filetype; the guard adds noise.

Apply this diff:

-      condition = "input['loadpage-filetype'] && input['loadpage-DDA_DIA'] == 'LType' && input['loadpage-filetype'] !== 'sample' && input['loadpage-filetype'] !== 'MRF'",
+      condition = "input['loadpage-filetype'] && input['loadpage-DDA_DIA'] == 'LType' && input['loadpage-filetype'] !== 'sample'",

238-246: Redundant condition duplication

(input['loadpage-BIO'] != 'PTM' && (input['loadpage-BIO'] != 'PTM' ...)) repeats the same predicate.

Apply this diff:

-      condition = "input['loadpage-filetype'] == 'msstats' && (input['loadpage-BIO'] != 'PTM' && (input['loadpage-BIO'] != 'PTM' && input['loadpage-DDA_DIA'] != 'TMT'))",
+      condition = "input['loadpage-filetype'] == 'msstats' && input['loadpage-BIO'] != 'PTM' && input['loadpage-DDA_DIA'] != 'TMT'",
man/loadpageUI.Rd (1)

5-5: Title wording nit.

Avoid repeating “UI.” Suggest “Load page UI module for data selection and upload.”

-\title{Loadpage UI module for data selection and upload UI.}
+\title{Load page UI module for data selection and upload.}
R/utils.R (2)

527-531: Coerce Parquet read to data.frame to match downstream expectations.

arrow::read_parquet may not return a base data.frame; DIANNtoMSstatsFormat typically expects a data.frame.

-      if (getFileExtension(input$dianndata$name) %in% c("parquet", "pq")) {
-        data = read_parquet(input$dianndata$datapath)
+      if (getFileExtension(input$dianndata$name) %in% c("parquet", "pq")) {
+        data = as.data.frame(read_parquet(input$dianndata$datapath))
       } else {
         data = read.csv(input$dianndata$datapath, sep=input$sep_dianndata)
       }

539-543: Guard quantificationColumn when DIANN 2+ toggle is off.

Avoid passing NULL/empty; fall back to "auto". Also be robust to NULL checkbox.

-      quantificationColumn = "auto"
-      if (!input$diann_2plus) {
-        quantificationColumn = input$intensity_column
-      }
+      quantificationColumn <- "auto"
+      if (!isTRUE(input$diann_2plus)) {
+        quantificationColumn <- if (!is.null(input$intensity_column) && nzchar(input$intensity_column)) {
+          input$intensity_column
+        } else "auto"
+      }
tests/testthat/test-module-loadpage-ui.R (2)

8-11: Avoid brittle deep indexing into tag tree.

Indexing via result[[1]][[4]][[1]] is fragile. Match class/name via HTML string or tagQuery.

-  # Should contain a fluidPage as the main structure
-  expect_equal(result[[1]][[4]][[1]]$name, "div")
-  expect_true("container-fluid" %in% result[[1]][[4]][[1]]$attribs$class)
+  # Should contain a fluidPage/container-fluid somewhere in the structure
+  html_output <- as.character(result)
+  expect_true(grepl("container-fluid", html_output, fixed = TRUE))

85-109: Minor: clarify what’s asserted for the upload button.

The comment says “disabled initially,” but the test only checks presence. Either assert disabled attribute or adjust the comment.

-  # Upload button should be disabled initially (using shinyjs)
+  # Upload button should exist (state handled dynamically by shinyjs)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fdef91a and 0654400.

📒 Files selected for processing (8)
  • DESCRIPTION (1 hunks)
  • NAMESPACE (1 hunks)
  • R/MSstatsShiny.R (1 hunks)
  • R/module-loadpage-ui.R (2 hunks)
  • R/utils.R (2 hunks)
  • man/MSstatsShiny.Rd (1 hunks)
  • man/loadpageUI.Rd (1 hunks)
  • tests/testthat/test-module-loadpage-ui.R (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: Run pr agent on every pull request
🔇 Additional comments (6)
man/MSstatsShiny.Rd (1)

59-59: Docs correctly marked as internal

Adding \keyword{internal} aligns the man page with the internal package pattern.

DESCRIPTION (1)

30-30: Arrow import looks good

Adding arrow to Imports matches the new Parquet support.

R/module-loadpage-ui.R (2)

486-492: Verify and update server-side references for the renamed inputs
I wasn’t able to locate any occurrences of input$remove, input$remove_1pep_charge, or input$remove_1feature in the server code. Please manually confirm and update any reactive expressions, observers, or render functions that depend on the “remove” checkbox so they reference the new IDs (remove_1pep_charge and remove_1feature). This will ensure you don’t introduce undefined behavior or break existing functionality.


489-489: Ignore the uniqe_peptides typo warning
The only occurrence of uniqe_peptides is the UI declaration—there are no references to either uniqe_peptides or unique_peptides in your server code. If you intended this checkbox to control a “unique peptides” filter, you’ll need to implement the corresponding server-side logic; otherwise you can remove this unused UI element.

Likely an incorrect or invalid review comment.

NAMESPACE (1)

51-51: Importing arrow::read_parquet is correct

Matches DESCRIPTION and enables Parquet ingestion code paths.

tests/testthat/test-module-loadpage-ui.R (1)

16-35: Namespacing check looks good.

Verifies key IDs carry the module prefix. Keep.

Comment thread R/module-loadpage-ui.R Outdated
Comment thread R/module-loadpage-ui.R Outdated
Comment thread R/module-loadpage-ui.R Outdated
Comment thread R/module-loadpage-ui.R Outdated
Comment thread R/module-loadpage-ui.R
Comment thread R/utils.R
Comment thread R/utils.R
Comment on lines +64 to +82
test_that("loadpageUI includes required conditional panels for different workflows", {
# Test that key conditional panels exist for different analysis types
result <- loadpageUI("test")
html_output <- as.character(result)

# Check for conditional panel conditions that handle different workflows
# Note: HTML entities encode single quotes as &#39;
expected_conditions <- c(
"input[&#39;loadpage-filetype&#39;] == &#39;sample&#39;", # Sample data panels
"input[&#39;loadpage-BIO&#39;] != &#39;PTM&#39;", # Non-PTM workflows
"input[&#39;loadpage-filetype&#39;] == &#39;maxq&#39;", # MaxQuant workflow
"input[&#39;loadpage-DDA_DIA&#39;] == &#39;TMT&#39;", # TMT labeling
"input[&#39;loadpage-filetype&#39;] == &#39;sky&#39;" # Skyline workflow
)

for(condition in expected_conditions) {
expect_true(grepl(condition, html_output, fixed = TRUE),
info = paste("Missing conditional panel for:", condition))
}
})
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Aug 27, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Do not hardcode 'loadpage-' in conditionalPanel conditions.

If the module id is "test", conditions should reference test-, not loadpage-. This test may mask real namespacing issues.

-  expected_conditions <- c(
-    "input[&#39;loadpage-filetype&#39;] == &#39;sample&#39;",     # Sample data panels
-    "input[&#39;loadpage-BIO&#39;] != &#39;PTM&#39;",             # Non-PTM workflows
-    "input[&#39;loadpage-filetype&#39;] == &#39;maxq&#39;",       # MaxQuant workflow
-    "input[&#39;loadpage-DDA_DIA&#39;] == &#39;TMT&#39;",         # TMT labeling
-    "input[&#39;loadpage-filetype&#39;] == &#39;sky&#39;"        # Skyline workflow
-  )
+  ns <- shiny::NS("test")
+  expected_conditions <- c(
+    paste0("input[&#39;", ns(\"filetype\"), "&#39;] == &#39;sample&#39;"),
+    paste0("input[&#39;", ns(\"BIO\"), "&#39;] != &#39;PTM&#39;"),
+    paste0("input[&#39;", ns(\"filetype\"), "&#39;] == &#39;maxq&#39;"),
+    paste0("input[&#39;", ns(\"DDA_DIA\"), "&#39;] == &#39;TMT&#39;"),
+    paste0("input[&#39;", ns(\"filetype\"), "&#39;] == &#39;sky&#39;")
+  )

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

🤖 Prompt for AI Agents
In tests/testthat/test-module-loadpage-ui.R around lines 64 to 83, the test
hardcodes conditionalPanel strings with the prefix 'loadpage-' which will fail
when the module id is different; update the test to build expected_conditions
dynamically from the module id used in the call (e.g., use the id variable
"test" to prefix each control name or use Shiny::NS to generate namespaced IDs),
ensuring you still match the HTML-encoded single quotes (&#39;) in the expected
strings before running grepl.

Copy link
Copy Markdown
Contributor Author

@tonywu1999 tonywu1999 Aug 28, 2025

Choose a reason for hiding this comment

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

I'll deal with this in another PR, namespace is chaos here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@tonywu1999 Understood! Since you'll be addressing this in a separate PR, would you like me to create a GitHub issue to track this testing improvement? It would help ensure the hardcoded 'loadpage-' conditional panel conditions get properly updated to use dynamic namespacing.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
R/utils.R (1)

198-198: Confirm DESCRIPTION/NAMESPACE consistency for arrow.

Since read_parquet is imported, ensure arrow is listed in DESCRIPTION Imports to satisfy R CMD check.

Run:

#!/bin/bash
rg -nP '^(Imports|Suggests):' DESCRIPTION
rg -nP '^\s*arrow\b' DESCRIPTION
rg -nP 'importFrom\(\s*arrow,\s*read_parquet\s*\)' NAMESPACE
🧹 Nitpick comments (1)
R/utils.R (1)

518-523: Simplify DIANN parquet detection and standardize output
input$dianndata is single-file (multiple=FALSE), so getFileExtension(input$dianndata$name) always returns length 1—drop the extra length guard. Wrap read_parquet() in as.data.frame() to guarantee a data.frame across arrow versions. Consider updating the DIANN example in getDataCode() to mention parquet support.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0654400 and b91aac9.

📒 Files selected for processing (4)
  • DESCRIPTION (1 hunks)
  • NAMESPACE (2 hunks)
  • R/module-loadpage-ui.R (2 hunks)
  • R/utils.R (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • NAMESPACE
  • R/module-loadpage-ui.R
  • DESCRIPTION
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
R/utils.R (1)

192-196: NA-safe, normalized extension extraction — LGTM.

Handles NULL/NA/empty and normalizes via tools::file_ext; good improvement.

Comment thread R/utils.R
Comment thread R/utils.R Outdated
Comment thread R/utils.R
Comment on lines 535 to 545
mydata = DIANNtoMSstatsFormat(data,
annotation = getAnnot(input),
qvalue_cutoff = qvalue_cutoff,
MBR = MBR,
removeProtein_with1Feature = TRUE,
removeFewMeasurements = FALSE,
use_log_file = FALSE
use_log_file = FALSE,
quantificationColumn = quantificationColumn
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Guard for MSstats versions lacking quantificationColumn; pass conditionally.

Older DIANNtoMSstatsFormat may not accept quantificationColumn. Prevent runtime error by building args dynamically (or bump MSstats minimal version).

Apply:

-      mydata = DIANNtoMSstatsFormat(data,
-                                    annotation = getAnnot(input),
-                                    qvalue_cutoff = qvalue_cutoff,
-                                    MBR = MBR,
-                                    removeProtein_with1Feature = TRUE,
-                                    removeFewMeasurements = FALSE,
-                                    use_log_file = FALSE,
-                                    quantificationColumn = quantificationColumn
-      )
+      diann_args = list(
+        data,
+        annotation = getAnnot(input),
+        qvalue_cutoff = qvalue_cutoff,
+        MBR = MBR,
+        removeProtein_with1Feature = TRUE,
+        removeFewMeasurements = FALSE,
+        use_log_file = FALSE
+      )
+      if ("quantificationColumn" %in% names(formals(DIANNtoMSstatsFormat))) {
+        diann_args$quantificationColumn = quantificationColumn
+      }
+      mydata = do.call(DIANNtoMSstatsFormat, diann_args)

Alternatively, ensure DESCRIPTION pins MSstats to a version that supports quantificationColumn; verify with:


🏁 Script executed:

#!/bin/bash
rg -nP '^Imports:' DESCRIPTION
rg -nP '^\s*MSstats\b' DESCRIPTION

Length of output: 242


Guard the quantificationColumn argument in DIANNtoMSstatsFormat or pin MSstats’s minimal version
Build the call arguments dynamically and only add quantificationColumn if it’s in the function formals (see diff below), or update DESCRIPTION to import MSstats at a version that supports this parameter.

-      mydata = DIANNtoMSstatsFormat(data,
-                                    annotation = getAnnot(input),
-                                    qvalue_cutoff = qvalue_cutoff,
-                                    MBR = MBR,
-                                    removeProtein_with1Feature = TRUE,
-                                    removeFewMeasurements = FALSE,
-                                    use_log_file = FALSE,
-                                    quantificationColumn = quantificationColumn
-      )
+      diann_args = list(
+        data,
+        annotation = getAnnot(input),
+        qvalue_cutoff = qvalue_cutoff,
+        MBR = MBR,
+        removeProtein_with1Feature = TRUE,
+        removeFewMeasurements = FALSE,
+        use_log_file = FALSE
+      )
+      if ("quantificationColumn" %in% names(formals(DIANNtoMSstatsFormat))) {
+        diann_args$quantificationColumn = quantificationColumn
+      }
+      mydata = do.call(DIANNtoMSstatsFormat, diann_args)

Alternatively, in DESCRIPTION:

- Imports: …, MSstats,
+ Imports: …, MSstats (>= 4.0.0),
📝 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
mydata = DIANNtoMSstatsFormat(data,
annotation = getAnnot(input),
qvalue_cutoff = qvalue_cutoff,
MBR = MBR,
removeProtein_with1Feature = TRUE,
removeFewMeasurements = FALSE,
use_log_file = FALSE
use_log_file = FALSE,
quantificationColumn = quantificationColumn
)
# Build DIANNtoMSstatsFormat args and include quantificationColumn only if supported
diann_args = list(
data,
annotation = getAnnot(input),
qvalue_cutoff = qvalue_cutoff,
MBR = MBR,
removeProtein_with1Feature = TRUE,
removeFewMeasurements = FALSE,
use_log_file = FALSE
)
if ("quantificationColumn" %in% names(formals(DIANNtoMSstatsFormat))) {
diann_args$quantificationColumn = quantificationColumn
}
mydata = do.call(DIANNtoMSstatsFormat, diann_args)
🤖 Prompt for AI Agents
In R/utils.R around lines 535 to 543, the call to DIANNtoMSstatsFormat
unconditionally passes quantificationColumn which breaks on older MSstats;
either guard this argument or pin MSstats. Modify the call to build arguments
dynamically (e.g., create a list of args and only add quantificationColumn if
"quantificationColumn" is present in names(formals(DIANNtoMSstatsFormat)), then
call DIANNtoMSstatsFormat via do.call with that args list), or alternatively
update DESCRIPTION to import MSstats at a minimal version that supports
quantificationColumn (bump Imports/Depends with >= the required MSstats
version).

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

♻️ Duplicate comments (1)
tests/testthat/test-module-loadpage-ui.R (1)

64-83: Do not hardcode 'loadpage-' in conditionalPanel conditions; derive from NS(id)

Use the module id used in this test ("test") to build expected conditions so the test actually validates namespacing.

-  expected_conditions <- c(
-    "input[&#39;loadpage-filetype&#39;] == &#39;sample&#39;",     # Sample data panels
-    "input[&#39;loadpage-BIO&#39;] != &#39;PTM&#39;",             # Non-PTM workflows
-    "input[&#39;loadpage-filetype&#39;] == &#39;maxq&#39;",       # MaxQuant workflow
-    "input[&#39;loadpage-DDA_DIA&#39;] == &#39;TMT&#39;",         # TMT labeling
-    "input[&#39;loadpage-filetype&#39;] == &#39;sky&#39;"        # Skyline workflow
-  )
+  ns <- shiny::NS("test")
+  expected_conditions <- c(
+    sprintf("input[&#39;%s&#39;] == &#39;sample&#39;", ns("filetype")),
+    sprintf("input[&#39;%s&#39;] != &#39;PTM&#39;",    ns("BIO")),
+    sprintf("input[&#39;%s&#39;] == &#39;maxq&#39;",  ns("filetype")),
+    sprintf("input[&#39;%s&#39;] == &#39;TMT&#39;",   ns("DDA_DIA")),
+    sprintf("input[&#39;%s&#39;] == &#39;sky&#39;",   ns("filetype"))
+  )
🧹 Nitpick comments (1)
tests/testthat/test-module-loadpage-ui.R (1)

201-201: Use shiny::NS consistently

Be explicit to avoid accidental masking and to make intent clear.

-  controls <- create_main_selection_controls(NS("test"))
+  controls <- create_main_selection_controls(shiny::NS("test"))
@@
-  selection <- create_label_free_type_selection(NS("test"))
+  selection <- create_label_free_type_selection(shiny::NS("test"))
@@
-  uploads <- create_standard_uploads(NS("test"))
+  uploads <- create_standard_uploads(shiny::NS("test"))
@@
-  uploads <- create_msstats_uploads(NS("test"))
+  uploads <- create_msstats_uploads(shiny::NS("test"))
@@
-  uploads <- create_ptm_fragpipe_uploads(NS("test"))
+  uploads <- create_ptm_fragpipe_uploads(shiny::NS("test"))
@@
-  uploads <- create_maxquant_uploads(NS("test"))
+  uploads <- create_maxquant_uploads(shiny::NS("test"))
@@
-  uploads <- create_ptm_uploads(NS("test"))
+  uploads <- create_ptm_uploads(shiny::NS("test"))
@@
-  options <- create_processing_options(NS("test"))
+  options <- create_processing_options(shiny::NS("test"))
@@
-  options <- create_quality_filtering_options(NS("test"))
+  options <- create_quality_filtering_options(shiny::NS("test"))
@@
-  buttons <- create_separator_buttons(NS("test"), "sep_test")
+  buttons <- create_separator_buttons(shiny::NS("test"), "sep_test")

Also applies to: 231-231, 247-247, 262-262, 279-279, 300-300, 314-314, 332-332, 343-343, 355-355

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b91aac9 and 0172037.

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

16-35: LGTM: namespaced ID presence checks are solid


134-151: Tests call internal helpers; confirm export or use :::/getFromNamespace

If helpers like create_header_content() are internal (@nord, not exported), direct calls will fail under R CMD check. Options:

  • Use MSstatsShiny:::create_header_content(), etc.
  • Or define a local helper once:
    ns_get <- function(nm) getFromNamespace(nm, "MSstatsShiny")

Then call ns_get("create_header_content")(). Let me know if you want a patch to update all invocations.

Also applies to: 153-170, 171-197, 199-227, 229-244, 245-260, 261-276, 277-297, 298-311, 312-329, 330-340, 341-352, 353-367, 368-386, 388-402

Comment thread tests/testthat/test-module-loadpage-ui.R Outdated
Comment thread tests/testthat/test-module-loadpage-ui.R Outdated
Comment thread tests/testthat/test-module-loadpage-ui.R
Comment thread tests/testthat/test-module-loadpage-ui.R
Comment thread tests/testthat/test-module-loadpage-ui.R
@Vitek-Lab Vitek-Lab deleted a comment from coderabbitai Bot Aug 28, 2025
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (11)
tests/testthat/test-module-loadpage-ui.R (11)

248-261: Add explicit Shiny namespace prefix.

Apply this diff for consistency:

-  uploads <- create_msstats_uploads(NS("test"))
+  uploads <- create_msstats_uploads(shiny::NS("test"))

264-282: Add explicit Shiny namespace prefix.

Apply this diff for consistency:

-  uploads <- create_ptm_fragpipe_uploads(NS("test"))
+  uploads <- create_ptm_fragpipe_uploads(shiny::NS("test"))

285-296: Add explicit Shiny namespace prefix.

Apply this diff for consistency:

-  uploads <- create_maxquant_uploads(NS("test"))
+  uploads <- create_maxquant_uploads(shiny::NS("test"))

299-314: Add explicit Shiny namespace prefix.

Apply this diff for consistency:

-  uploads <- create_ptm_uploads(NS("test"))
+  uploads <- create_ptm_uploads(shiny::NS("test"))

317-325: Add explicit Shiny namespace prefix throughout.

Multiple test functions use unqualified NS() calls.

Apply this diff to add consistent namespacing:

-  options <- create_processing_options(NS("test"))
+  options <- create_processing_options(shiny::NS("test"))
-  options <- create_quality_filtering_options(NS("test"))
+  options <- create_quality_filtering_options(shiny::NS("test"))
-  buttons <- create_separator_buttons(NS("test"), "sep_test")
+  buttons <- create_separator_buttons(shiny::NS("test"), "sep_test")
-  controls <- create_main_selection_controls(NS("test"))
+  controls <- create_main_selection_controls(shiny::NS("test"))
-  controls <- create_main_selection_controls(NS("test"))
+  controls <- create_main_selection_controls(shiny::NS("test"))

Also applies to: 328-337, 340-352, 355-372, 375-387


110-118: Prefix Shiny functions to avoid relying on search path.

The code calls fluidPage and potentially other Shiny functions without explicit namespace qualification, but there's no evidence of library(shiny) being called in the test environment.

Apply this diff to add explicit namespacing:

-  ui_output <- loadpageUI("test")
+  ui_output <- loadpageUI("test")
+  # Note: Verify fluidPage calls in helper functions use shiny:: prefix

Since this affects the create_test_app helper (which isn't shown in this file), ensure all Shiny function calls use shiny:: prefix or add library(shiny) to a test helper file.


169-175: Fix list-to-string coercion in CSS content validation.

The paste() function on a list of tags doesn't reliably render HTML content. The children should be properly converted to character strings first.

Apply this diff to fix the coercion:

-  children_html <- paste(css_children, collapse = " ")
-  expect_true(grepl("background-color:orange", children_html))
-  expect_true(grepl("proceed1", children_html))
-  expect_true(grepl("reset1", children_html))
-  expect_true(grepl("style.css", children_html))
+  children_html <- paste(vapply(css_children, as.character, character(1)), collapse = " ")
+  expect_true(grepl("background-color:orange", children_html))
+  expect_true(grepl("proceed1", children_html))
+  expect_true(grepl("reset1", children_html))
+  expect_true(grepl("style\\.css", children_html))

186-212: Use explicit shiny:: namespace prefix consistently.

The NS("test") call should be prefixed with shiny:: to avoid dependency on the search path.

Apply this diff:

-  controls <- create_main_selection_controls(NS("test"))
+  controls <- create_main_selection_controls(shiny::NS("test"))

217-229: Fix hardcoded namespace in conditional logic assertions.

The test checks for "loadpage-BIO" and "loadpage-filetype" without considering the actual namespace used in the test.

Apply this diff to use proper namespacing:

-  selection <- create_label_free_type_selection(NS("test"))
+  selection <- create_label_free_type_selection(shiny::NS("test"))
   selection_html <- as.character(selection)
   
   expect_true(grepl("shiny-panel-conditional", selection_html))
@@ -225,8 +225,9 @@
   expect_true(grepl("SRM/PRM", selection_html))
   
   # Check conditional logic
-  expect_true(grepl("loadpage-BIO", selection_html))
-  expect_true(grepl("loadpage-filetype", selection_html))
+  ns <- shiny::NS("test")
+  expect_true(grepl(ns("BIO"), selection_html, fixed = TRUE))
+  expect_true(grepl(ns("filetype"), selection_html, fixed = TRUE))

232-245: Fix hardcoded namespace and improve ID matching precision.

The test uses hardcoded "loadpage-filetype" and very loose token matching that may match unrelated content.

Apply this diff to improve precision:

-  uploads <- create_standard_uploads(NS("test"))
+  uploads <- create_standard_uploads(shiny::NS("test"))
   uploads_html <- as.character(uploads)
   
   expect_true(grepl("Upload quantification dataset", uploads_html))
   expect_true(grepl("shiny-input-file", uploads_html))
   expect_true(grepl("test-data", uploads_html))
   
-  # Check conditional logic for multiple file types
-  expect_true(grepl("loadpage-filetype", uploads_html))
-  expect_true(grepl("prog", uploads_html))
-  expect_true(grepl("PD", uploads_html))
-  expect_true(grepl("phil", uploads_html))
+  # Check for namespaced filetype reference and specific file type tokens
+  expect_true(grepl(shiny::NS("test")("filetype"), uploads_html, fixed = TRUE))
+  expect_true(grepl("id=\\\"test-(prog|PD|phil)\\\"", uploads_html))

390-395: Update test expectations for DIANN Parquet support.

Based on the PR objectives, DIANN 2.0 adds support for parquet files, which may legitimately include accept attributes. The current test may fail if parquet support is properly implemented.

Apply this diff to accommodate parquet support:

-  # Test CSV-only inputs
-  annot_input <- create_standard_annotation_uploads(NS("test"))
+  # Test CSV-only inputs  
+  annot_input <- create_standard_annotation_uploads(shiny::NS("test"))
   annot_html <- as.character(annot_input)
-  expect_true(grepl('accept=.*csv', annot_html))
+  # Annotation files should still be CSV-only
+  expect_true(grepl('accept=.*csv', annot_html))
+  # But standard uploads may now include parquet for DIANN
+  standard_input <- create_standard_uploads(shiny::NS("test"))  
+  standard_html <- as.character(standard_input)
+  expect_true(grepl("\\.parquet", standard_html) || grepl("\\.csv", standard_html))
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0172037 and 28a59f1.

📒 Files selected for processing (1)
  • tests/testthat/test-module-loadpage-ui.R (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

Comment on lines +1 to +395
test_that("loadpageUI returns a valid tagList with fluidPage structure", {
# Test basic function execution and structure
result <- loadpageUI("test")

# Should return a tagList
expect_s3_class(result, "shiny.tag.list")

result_html = as.character(result)
expect_true(grepl("div", result_html))

# Should not be NULL or empty
expect_true(length(result) > 0)
})

test_that("loadpageUI generates correct namespaced input IDs", {
# Test that all input elements use proper namespacing
result <- loadpageUI("testmodule")
html_output <- as.character(result)

# Check for key namespaced input IDs that should be present
expected_ids <- c(
"testmodule-BIO", # Biological question radio buttons
"testmodule-DDA_DIA", # Label type radio buttons
"testmodule-filetype", # File type radio buttons
"testmodule-proceed1" # Upload button
)

for(id in expected_ids) {
expect_true(
grepl(id, html_output, fixed = TRUE),
info = paste("Missing namespaced ID:", id)
)
}
})

test_that("loadpageUI contains all required radio button choices", {
# Test that essential radio button options are present
result <- loadpageUI("test")
html_output <- as.character(result)

# Check biological question options
bio_options <- c("Protein", "Peptide", "PTM")
for(option in bio_options) {
expect_true(grepl(option, html_output),
info = paste("Missing biological option:", option))
}

# Check label type options
label_options <- c("Label-Free", "TMT")
for(option in label_options) {
expect_true(grepl(option, html_output),
info = paste("Missing label option:", option))
}

# Check file type options (sample a few key ones)
file_options <- c("MaxQuant", "Skyline", "MSstats Format")
for(option in file_options) {
expect_true(grepl(option, html_output),
info = paste("Missing file type option:", option))
}
})

test_that("loadpageUI includes required conditional panels for different workflows", {
# Test that key conditional panels exist for different analysis types
result <- loadpageUI("test")
html_output <- as.character(result)

# Check for conditional panel conditions that handle different workflows
# Note: HTML entities encode single quotes as &#39;
expected_conditions <- c(
"input[&#39;loadpage-filetype&#39;] == &#39;sample&#39;", # Sample data panels
"input[&#39;loadpage-BIO&#39;] != &#39;PTM&#39;", # Non-PTM workflows
"input[&#39;loadpage-filetype&#39;] == &#39;maxq&#39;", # MaxQuant workflow
"input[&#39;loadpage-DDA_DIA&#39;] == &#39;TMT&#39;", # TMT labeling
"input[&#39;loadpage-filetype&#39;] == &#39;sky&#39;" # Skyline workflow
)

for(condition in expected_conditions) {
expect_true(grepl(condition, html_output, fixed = TRUE),
info = paste("Missing conditional panel for:", condition))
}
})

test_that("loadpageUI properly handles file input elements and validation", {
# Test that file inputs are properly configured
result <- loadpageUI("test")
html_output <- as.character(result)

# Should contain file input elements
expect_true(grepl('type="file"', html_output),
"No file input elements found")

# Upload button should be disabled initially (using shinyjs)
expect_true(grepl("proceed1", html_output),
"Upload button not found")

# Check for separator radio buttons for file parsing
expect_true(grepl("sep_data", html_output) || grepl("Column separator", html_output),
"File separator options not found")

# Should include help text and external links
expect_true(grepl("User Guide", html_output),
"Help documentation links not found")

# Should include file size warnings
expect_true(grepl("250 MB", html_output),
"File size limit warning not found")
})

# Test suite for loadpage UI module
test_that("loadpageUI creates proper structure", {
# Test the main UI structure
ui_output <- loadpageUI("test")

expect_s3_class(ui_output, "shiny.tag.list")
expect_true(any(grepl("container-fluid", as.character(ui_output))))
expect_true(any(grepl("Upload data", as.character(ui_output))))
})

# Tests for create_header_content()
test_that("create_header_content includes required elements", {
header <- create_header_content()
header_html <- as.character(header)

# Check for key text content
expect_true(grepl("MSstats Pipeline", header_html))
expect_true(grepl("User Guide", header_html))
expect_true(grepl("MSstatsPTM", header_html))
expect_true(grepl("CSV/TSV format", header_html))
expect_true(grepl("250 MB", header_html))

# Check for external links
expect_true(grepl("msstats.org", header_html))
expect_true(grepl("bioconductor.org", header_html))
expect_true(grepl('target="_blank"', header_html))
})

# Tests for create_sample_dataset_descriptions()
test_that("create_sample_dataset_descriptions creates conditional panels", {
descriptions <- create_sample_dataset_descriptions()
descriptions_html <- as.character(descriptions)

# Check for conditional panels
expect_true(grepl("shiny-panel-conditional", descriptions_html))

# Check for specific dataset references
expect_true(grepl("DDA acquisition", descriptions_html))
expect_true(grepl("DIA acquisition", descriptions_html))
expect_true(grepl("SRM/PRM acquisition", descriptions_html))

# Check for publication links
expect_true(grepl("Choi, M. et al", descriptions_html))
expect_true(grepl("Selevsek, N. et al", descriptions_html))
expect_true(grepl("Picotti, P. et al", descriptions_html))
})

# Tests for create_css_styling()
test_that("create_css_styling includes required CSS", {
css <- create_css_styling()

# Check that it's a proper tag structure (not character conversion)
expect_s3_class(css, "shiny.tag")
expect_equal(css$name, "head")

# Check the children elements contain the expected content
css_children <- css$children
expect_true(length(css_children) >= 3) # Should have 3 children (2 styles + 1 link)

# Convert children to string to check content
children_html <- paste(css_children, collapse = " ")
expect_true(grepl("background-color:orange", children_html))
expect_true(grepl("proceed1", children_html))
expect_true(grepl("reset1", children_html))
expect_true(grepl("style.css", children_html))

# Check that it's a head tag with proper structure
expect_true(any(sapply(css_children, function(x) {
if (is.list(x) && !is.null(x$name)) {
return(x$name == "style" || x$name == "link")
}
FALSE
})))
})

# Tests for create_main_selection_controls()
test_that("create_main_selection_controls creates proper radio buttons", {
controls <- create_main_selection_controls(NS("test"))
controls_html <- as.character(controls)

# Check for biological question options
expect_true(grepl("Biological Question", controls_html))
expect_true(grepl("Protein", controls_html))
expect_true(grepl("Peptide", controls_html))
expect_true(grepl("PTM", controls_html))

# Check for label type options
expect_true(grepl("Label Type", controls_html))
expect_true(grepl("Label-Free", controls_html))
expect_true(grepl("TMT", controls_html))

# Check for file type options
expect_true(grepl("Type of File", controls_html))
expect_true(grepl("MSstats Format", controls_html))
expect_true(grepl("Skyline", controls_html))
expect_true(grepl("MaxQuant", controls_html))
expect_true(grepl("FragPipe", controls_html))
expect_true(grepl("DIANN", controls_html))

# Check for proper namespace
expect_true(grepl("test-BIO", controls_html))
expect_true(grepl("test-DDA_DIA", controls_html))
expect_true(grepl("test-filetype", controls_html))
})

# Tests for create_label_free_type_selection()
test_that("create_label_free_type_selection creates conditional panel", {
selection <- create_label_free_type_selection(NS("test"))
selection_html <- as.character(selection)

expect_true(grepl("shiny-panel-conditional", selection_html))
expect_true(grepl("Type of Label-Free type", selection_html))
expect_true(grepl("DDA", selection_html))
expect_true(grepl("DIA", selection_html))
expect_true(grepl("SRM/PRM", selection_html))

# Check conditional logic
expect_true(grepl("loadpage-BIO", selection_html))
expect_true(grepl("loadpage-filetype", selection_html))
})

# Tests for create_standard_uploads()
test_that("create_standard_uploads creates file input with conditions", {
uploads <- create_standard_uploads(NS("test"))
uploads_html <- as.character(uploads)

expect_true(grepl("Upload quantification dataset", uploads_html))
expect_true(grepl("shiny-input-file", uploads_html))
expect_true(grepl("test-data", uploads_html))

# Check conditional logic for multiple file types
expect_true(grepl("loadpage-filetype", uploads_html))
expect_true(grepl("prog", uploads_html))
expect_true(grepl("PD", uploads_html))
expect_true(grepl("phil", uploads_html))
})

# Tests for create_msstats_uploads()
test_that("create_msstats_uploads creates different inputs for regular and PTM", {
uploads <- create_msstats_uploads(NS("test"))
uploads_html <- as.character(uploads)

# Check for regular MSstats format
expect_true(grepl("Upload data in MSstats Format", uploads_html))
expect_true(grepl("test-msstatsdata", uploads_html))

# Check for PTM MSstats format
expect_true(grepl("Upload PTM data in MSstats Format", uploads_html))
expect_true(grepl("test-msstatsptmdata", uploads_html))
expect_true(grepl("Upload unmodified data in MSstats Format", uploads_html))
expect_true(grepl("test-unmod", uploads_html))
})

# Tests for create_ptm_fragpipe_uploads()
test_that("create_ptm_fragpipe_uploads creates comprehensive PTM options", {
uploads <- create_ptm_fragpipe_uploads(NS("test"))
uploads_html <- as.character(uploads)

# Check for required uploads
expect_true(grepl("Upload PTM msstats dataset", uploads_html))
expect_true(grepl("Upload PTM annotation file", uploads_html))
expect_true(grepl("Upload global profiling msstats dataset", uploads_html))
expect_true(grepl("Upload global profiling annotation file", uploads_html))

# Check for processing options
expect_true(grepl("modification id column", uploads_html))
expect_true(grepl("localization_cutoff", uploads_html))
expect_true(grepl("Remove unlocalized peptides", uploads_html))

# Check default values
expect_true(grepl("STY", uploads_html))
expect_true(grepl("\\.75", uploads_html))
})

# Tests for create_maxquant_uploads()
test_that("create_maxquant_uploads creates proper file inputs", {
uploads <- create_maxquant_uploads(NS("test"))
uploads_html <- as.character(uploads)

expect_true(grepl("Upload evidence.txt File", uploads_html))
expect_true(grepl("Upload proteinGroups.txt File", uploads_html))
expect_true(grepl("Upload annotation File", uploads_html))

expect_true(grepl("test-evidence", uploads_html))
expect_true(grepl("test-pGroup", uploads_html))
expect_true(grepl("test-annot1", uploads_html))
})

# Tests for create_ptm_uploads()
test_that("create_ptm_uploads creates PTM-specific inputs", {
uploads <- create_ptm_uploads(NS("test"))
uploads_html <- as.character(uploads)

# Check for PTM specific uploads
expect_true(grepl("Upload PTM input.txt File", uploads_html))
expect_true(grepl("Upload fasta File", uploads_html))
expect_true(grepl("Upload Unmodified Protein input.txt File", uploads_html))

# Check for modification labels
expect_true(grepl("Modification Label", uploads_html))
expect_true(grepl("FASTA file column name", uploads_html))

# Check default values
expect_true(grepl("uniprot_iso", uploads_html))
})

# Tests for create_processing_options()
test_that("create_processing_options creates TMT and label-free options", {
options <- create_processing_options(NS("test"))
options_html <- as.character(options)

# Check for processing options structure
expect_true(grepl("Select the options for pre-processing", options_html))
expect_true(grepl("Use unique peptides", options_html))
expect_true(grepl("Remove proteins with 1", options_html))
})

# Tests for create_quality_filtering_options()
test_that("create_quality_filtering_options creates filtering controls", {
options <- create_quality_filtering_options(NS("test"))
options_html <- as.character(options)

expect_true(grepl("Filter with Q-value", options_html))
expect_true(grepl("Filter with M-score", options_html))
expect_true(grepl("Q-value cutoff", options_html))
expect_true(grepl("M-score cutoff", options_html))
expect_true(grepl("MBR Enabled", options_html))
})

# Tests for create_separator_buttons()
test_that("create_separator_buttons creates proper radio buttons", {
buttons <- create_separator_buttons(NS("test"), "sep_test")
buttons_html <- as.character(buttons)

expect_true(grepl("Column separator", buttons_html))
expect_true(grepl("Comma", buttons_html))
expect_true(grepl("Semicolon", buttons_html))
expect_true(grepl("Tab", buttons_html))
expect_true(grepl("Pipe", buttons_html))

# Check for inline display
expect_true(grepl("radio-inline", buttons_html))
})

# Test order preservation in main selection controls
test_that("main selection controls maintain proper order", {
controls <- create_main_selection_controls(NS("test"))
controls_html <- as.character(controls)

# Find positions of each section
bio_pos <- regexpr("Biological Question", controls_html)
label_pos <- regexpr("Label Type", controls_html)
file_pos <- regexpr("Type of File", controls_html)

# Verify correct order
expect_true(bio_pos < label_pos)
expect_true(label_pos < file_pos)

# Verify numbered headings are in order
expect_true(grepl("1\\. Biological Question", controls_html))
expect_true(grepl("2\\. Label Type", controls_html))
expect_true(grepl("3\\. Type of File", controls_html))
})

# Test tooltip content is preserved
test_that("tooltips contain proper explanatory text", {
controls <- create_main_selection_controls(NS("test"))
controls_html <- as.character(controls)

# Check for tooltip explanations
expect_true(grepl("Select the biological question of interest", controls_html))
expect_true(grepl("Label-free will process all label-free acquisitions", controls_html))
expect_true(grepl("Choose the spectral processing tool used", controls_html))

# Check for icon-wrapper and icon-tooltip classes
expect_true(grepl("icon-wrapper", controls_html))
expect_true(grepl("icon-tooltip", controls_html))
})

# Test file input configurations
test_that("file inputs have proper accept attributes", {
# Test CSV-only inputs
annot_input <- create_standard_annotation_uploads(NS("test"))
annot_html <- as.character(annot_input)
expect_true(grepl('accept=.*csv', annot_html))
}) No newline at end of file
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Missing verification scripts for namespace consistency.

The tests have several namespace-related issues that should be verified across the codebase to ensure the UI functions properly handle different namespace scenarios.

Run the following script to verify namespace consistency:


🏁 Script executed:

#!/bin/bash
# Description: Verify that loadpageUI and helper functions handle namespacing consistently

echo "=== Checking for loadpage UI functions and namespace usage ==="
ast-grep --pattern $'function($id) {
  ns <- NS($id)
  $$$
}'

echo "=== Finding conditional panel conditions that reference input names ==="
rg -nP 'input\[.*?\].*?==' --type=r -A2 -B2

echo "=== Checking for hardcoded loadpage- references in UI code ==="
rg -n 'loadpage-' --type=r -g '!test*'

echo "=== Verifying shiny:: namespace usage in test files ==="
rg -n 'NS\(' tests/testthat/ -A1 -B1

Length of output: 43822


Replace hardcoded “loadpage-” prefixes in all conditionalPanel conditions with dynamic ns() calls
In module-loadpage-ui.R, update every condition = "input['loadpage-XXX'] …" to build the ID via ns("XXX") (e.g. paste0("input['", ns("filetype"), "'] == 'sample'")), and adjust the corresponding tests in test-module-loadpage-ui.R to assert against the actual namespaced IDs returned by loadpageUI("test").

🤖 Prompt for AI Agents
In tests/testthat/test-module-loadpage-ui.R lines 1-395, the tests expect
conditionalPanel conditions and namespaced IDs to use hardcoded "loadpage-*"
prefixes; update the UI module (module-loadpage-ui.R) to build conditionalPanel
condition strings with ns(), e.g. paste0("input['", ns("filetype"), "'] ==
'sample'"), and then change the tests to assert against the runtime namespaced
IDs produced by calling loadpageUI("test") (or using NS("test") where
appropriate) instead of checking for "loadpage-*" literals so tests validate the
actual namespaced IDs and condition strings.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant