Refactor the QC page#218
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe QC UI and server are split into helper modules for visibility, plots, summary results, downloads, and turnover ratios. Shared QC namespace constants are added, the UI uses hidden panels instead of conditional panels, and tests cover the new IDs and visibility helpers. ChangesQC Module Decomposition
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested labels: Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 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/module-qc-server.R`:
- Around line 67-83: The transition gating for proceed6 is ineffective because
`renderUI` in the `observeEvent(input$run, ...)` block creates an enabled
`actionButton`, so users can advance before preprocessing completes. Update the
`proceed6` button creation to start disabled or only render it once
`preprocess_data()` is ready, and keep the `enable("proceed6")` call tied to the
`observeEvent(preprocess_data(), ...)`/preprocessing completion path so
`StatsModel` can’t be reached early.
In `@R/module-qc-ui.R`:
- Around line 51-57: The selectInput for `ns("summarization")` in `module-qc-ui`
uses an invalid default value, since `selected = "log"` does not match any of
the listed choices. Update the default to one of the actual option values used
in that input (for example the value corresponding to the intended summarization
method) so the `summarization` control initializes deterministically in the TMT
branch.
In `@R/qc-server-downloads.R`:
- Around line 8-20: The download buttons are being enabled in
observeEvent(input$run), but the download handlers immediately rely on
preprocess_data(), so users can trigger exports before preprocessing has
finished. Move the enable/disable logic to the point where preprocessing
completion is known (for example, after the preprocess_data() result is ready)
and keep the buttons disabled until then; update the PTM/non-PTM branches
consistently in the related download handlers so they only become clickable once
the required data exists.
In `@R/qc-server-plots.R`:
- Around line 66-67: The download flow for QualityMetricsPlot bypasses the
intended MSstatsQualityMetricsPlot branch because qcPlot() removes input$which
too early, causing plotresult() to fail when it later expects that value. Update
the save/download logic so QualityMetricsPlot is either explicitly disabled or
handled as a special case before plotresult() is called, and ensure qcPlot(),
plotresult(), and MSstatsQualityMetricsPlot() route this plot type without
requiring input$which.
- Around line 148-150: The CI calculation in ConditionPlot is referencing the
wrong columns, so fix the stats table logic in the function that builds `tab` by
using `Number_of_Measurements` consistently and by using the computed `CI_width`
value instead of the nonexistent `tab$CI`. Update the `SE`, `CI_width`, and
`CI_Limits` assignments together so they all refer to the same created columns
and the table renders without missing-field errors.
- Around line 75-80: The PTM selector in qc-server-plots.R is building choices
from get_data()$PTM[1], which is a one-column object instead of the actual PTM
vector. Update the selectizeInput calls in the plotting logic to use the PTM
vector itself from get_data() so the dropdown shows real PTM options, and keep
the existing "ALL PROTEINS" entry only in the branch that needs it.
In `@R/qc-server-sidebar.R`:
- Around line 258-260: The observer using input$all_feat is stale because qcUI
no longer defines that input, so it can evaluate to length-zero and break.
Update the observer in qc-server-sidebar.R to use the current feature-selection
input (features_used) instead of all_feat, and keep the toggleState("n_feat",
...) logic aligned with the new UI state. Make sure the fix is applied in the
observer block that currently references input$all_feat.
- Around line 305-314: The PTM branch in max_feat() is counting a vector element
with nrow(), which returns NULL and can break sliderInput for PTM data. Update
the PTM path in max_feat() to use length() on get_data()$PTM[1] (or the
equivalent PTM vector) instead of nrow(unique(...)), and keep the non-PTM branch
unchanged so sliderInput(ns("n_feat")) always receives a valid numeric max.
- Around line 237-250: The censoring radio selectors in the observer are
currently global and the jQuery lookup is not module-scoped, so update the logic
in observe() to target the module’s namespaced input IDs/classes instead of
plain [value=NA], [value=0], and name=censInt. Use the same unique symbols in
this block, including loadpage_input(), enable("censInt"), disable(), and
runjs(), but scope all selector strings to the module namespace so only this
module’s radios are affected.
🪄 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: 9772cb1b-bae0-4ae1-a00d-9c705606511b
📒 Files selected for processing (10)
R/constants.RR/module-qc-server.RR/module-qc-ui.RR/qc-server-downloads.RR/qc-server-plots.RR/qc-server-sidebar.RR/qc-server-summary.RR/qc-server-turnover.Rtests/testthat/test-module-qc-ui.Rtests/testthat/test-qc-server-rendering.R
| selectInput(ns("summarization"), | ||
| h4("Summarization method",class = "icon-wrapper",icon("question-circle", lib = "font-awesome"), | ||
| div("Select method to be used for protein summarization. For details on each option please see Help tab", class = "icon-tooltip")), | ||
| c("MSstats" = "msstats", | ||
| "Tukeys median polish" = "MedianPolish", | ||
| "Log(Sum)" = "LogSum","Median" = "Median"), | ||
| selected = "log") |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use a valid default summarization value.
Line 57 sets selected = "log", but "log" is not one of this selectInput’s choices. Pick a real value so the default TMT branch state is deterministic.
Proposed fix
- selected = "log")
+ selected = "msstats")📝 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.
| selectInput(ns("summarization"), | |
| h4("Summarization method",class = "icon-wrapper",icon("question-circle", lib = "font-awesome"), | |
| div("Select method to be used for protein summarization. For details on each option please see Help tab", class = "icon-tooltip")), | |
| c("MSstats" = "msstats", | |
| "Tukeys median polish" = "MedianPolish", | |
| "Log(Sum)" = "LogSum","Median" = "Median"), | |
| selected = "log") | |
| selectInput(ns("summarization"), | |
| h4("Summarization method",class = "icon-wrapper",icon("question-circle", lib = "font-awesome"), | |
| div("Select method to be used for protein summarization. For details on each option please see Help tab", class = "icon-tooltip")), | |
| c("MSstats" = "msstats", | |
| "Tukeys median polish" = "MedianPolish", | |
| "Log(Sum)" = "LogSum","Median" = "Median"), | |
| selected = "msstats") |
🤖 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-qc-ui.R` around lines 51 - 57, The selectInput for
`ns("summarization")` in `module-qc-ui` uses an invalid default value, since
`selected = "log"` does not match any of the listed choices. Update the default
to one of the actual option values used in that input (for example the value
corresponding to the intended summarization method) so the `summarization`
control initializes deterministically in the TMT branch.
c57e24b to
498ae4b
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
R/qc-server-plots.R (1)
89-133: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winGuard on the
proteinargument, notinput$which.
plotresult()receivesprotein, but Line 89 still checksinput$which; when the selector is not initialized this can evaluateif (NULL != "")and error before returningNULL.Proposed fix
plotresult = function(saveFile, protein, summary, original, file) { - if (input$which != "") { + if (length(protein) == 0 || identical(protein, "")) { + return(NULL) + } + if (loadpage_input()$BIO == "PTM"){ plot = dataProcessPlotsPTM(preprocess_data(), ... return(plot) } - } - else { - return(NULL) - } }🤖 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/qc-server-plots.R` around lines 89 - 133, The guard in plotresult() is checking input$which instead of the protein argument, which can fail when the selector is unset. Update the initial conditional to validate protein directly, and keep the existing PTM/TMT/default branches unchanged inside plotresult() so the function returns NULL safely when protein is missing.
🤖 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/constants.R`:
- Around line 168-174: `NAMESPACE_EXPDES` is missing the `result_plot` shared id
that the expdes module still expects. Restore the `result_plot` entry in
`NAMESPACE_EXPDES` in constants, or if the id is being renamed/removed, update
both `module-expdes-ui.R` and `module-expdes-server.R` together so every
`NAMESPACE_EXPDES$result_plot` reference uses the same replacement symbol.
In `@R/qc-server-plots.R`:
- Around line 137-139: The statistics reactive is reading
preprocess_data()$ProteinLevelData and input$which without guarding for empty
startup state, so add req(preprocess_data(), input$which) at the start of
statistics() before any subsetting. Update the reactive in qc-server-plots.R so
renderTable(statistics()) only runs after the processed data and selection are
available, using the statistics() and preprocess_data() symbols as the main
touchpoints.
- Around line 195-202: The download handler in the plot export flow is still
calling plotresult() with address set to FALSE, so it stays on the Plotly path
while pdf(file) is open and can produce an empty export. Update the content
function in output$saveplot to pass the download path through to plotresult()
via its saveFile/address parameters, and ensure dev.off() is protected with
on.exit() if the PDF device remains open during plotting.
---
Duplicate comments:
In `@R/qc-server-plots.R`:
- Around line 89-133: The guard in plotresult() is checking input$which instead
of the protein argument, which can fail when the selector is unset. Update the
initial conditional to validate protein directly, and keep the existing
PTM/TMT/default branches unchanged inside plotresult() so the function returns
NULL safely when protein is missing.
🪄 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: 31f22ce7-ccd8-427f-bc71-6d45f650c263
📒 Files selected for processing (10)
R/constants.RR/module-qc-server.RR/module-qc-ui.RR/qc-server-downloads.RR/qc-server-plots.RR/qc-server-sidebar.RR/qc-server-summary.RR/qc-server-turnover.Rtests/testthat/test-module-qc-ui.Rtests/testthat/test-qc-server-rendering.R
🚧 Files skipped from review as they are similar to previous changes (8)
- tests/testthat/test-module-qc-ui.R
- R/qc-server-turnover.R
- R/module-qc-server.R
- R/qc-server-downloads.R
- R/qc-server-summary.R
- tests/testthat/test-qc-server-rendering.R
- R/module-qc-ui.R
- R/qc-server-sidebar.R
| if(loadpage_input()$DDA_DIA=='TMT'){ | ||
|
|
||
| write.csv(preprocess_data()$FeatureLevelData, file, row.names = FALSE) | ||
|
|
||
| } | ||
| else{ | ||
|
|
||
| write.csv(preprocess_data()$FeatureLevelData, file, row.names = FALSE) | ||
| } |
There was a problem hiding this comment.
This conditional is not needed right? It's the same line of code.
| enable("prepr_csv_prot") | ||
| enable("summ_csv_prot") | ||
| } else { | ||
| enable("prepr_csv") |
There was a problem hiding this comment.
Rename this to prep_feature_level_data_csv
| enable("summ_csv_prot") | ||
| } else { | ||
| enable("prepr_csv") | ||
| enable("summ_csv") |
There was a problem hiding this comment.
Rename this to prep_protein_level_data_csv
| enable("prepr_csv_ptm") | ||
| enable("summ_csv_ptm") | ||
| enable("prepr_csv_prot") | ||
| enable("summ_csv_prot") |
There was a problem hiding this comment.
Would rename to:
prep_feature_level_data_csv_ptm
prep_protein_level_data_csv_ptm
prep_feature_level_data_csv_global_proteome
prep_protein_level_data_csv_global_proteome
| unique(get_data()$PTM[1]))) | ||
| } else { | ||
| selectizeInput(ns("which"), "Show plot for", | ||
| choices = c("", unique(get_data()$PTM[1]))) |
There was a problem hiding this comment.
Rather than a number like get_data()$PTM[1], use get_data()$PTM$ProteinName
| # which protein to plot (will add "all" for QCPlot) | ||
| output$Which = renderUI({ | ||
| ns <- session$ns | ||
| req(input$type1) |
There was a problem hiding this comment.
Let's do something more descriptive than type1, maybe like qc_page_plot_type
| selectizeInput(ns("qm_protein"), | ||
| label = h5("Show plot for"), | ||
| choices = c("", protein_choices)) |
There was a problem hiding this comment.
I wonder if this can overlap with the code between lines 63 and 86, seems like duplicate code.
| statistics = reactive({ | ||
| sub = preprocess_data()$ProteinLevelData[which(preprocess_data()$ProteinLevelData$Protein == input$which),] | ||
| len = aggregate(sub$LogIntensities~sub$GROUP_ORIGINAL, length, data = sub) | ||
| colnames(len)[colnames(len)=="sub$LogIntensities"] = "Number_of_Measurements" | ||
| sd = aggregate(sub$LogIntensities~sub$GROUP_ORIGINAL, sd, data = sub) | ||
| colnames(sd)[colnames(sd)=="sub$LogIntensities"] = "Standard_Deviation" | ||
| mean = aggregate(sub$LogIntensities~sub$GROUP_ORIGINAL, mean, data = sub) | ||
| colnames(mean)[colnames(mean)=="sub$LogIntensities"] = "Mean" | ||
| tab = merge(len, sd, by="sub$GROUP_ORIGINAL") | ||
| tab = merge(mean, tab, by="sub$GROUP_ORIGINAL") | ||
| colnames(tab)[colnames(tab)=="sub$GROUP_ORIGINAL"] = "Condition" | ||
| SE = tab$Standard_Deviation/sqrt(tab$Number_of_Measurements) | ||
| tab$CI_width = qt(.975, df=tab$Number_of_Measurement)*SE | ||
| CI_Limits = c(tab$Mean-tab$CI, tab$Mean+tab$CI) | ||
|
|
||
| return(tab) | ||
| }) |
There was a problem hiding this comment.
Where is the condition plot used exactly? I don't recall there being an option for the condition plot.
My gut tells me this can be removed (and any reference to condition plots, I don't think they're useful tbh)
| ) | ||
| tagList( | ||
| op, | ||
| conditionalPanel(condition = "input['qc-type'] == 'ConditionPlot' && input['qc-which'] != ''", |
There was a problem hiding this comment.
I think this can be removed. I don't think we give an option for ConditionPlot.
| output = plotresult(FALSE, input$which, FALSE, TRUE, FALSE) | ||
| } | ||
| else if (input$summ == TRUE) { | ||
| output = plotresult(FALSE, input$which, TRUE, FALSE, FALSE) |
There was a problem hiding this comment.
Could you rename plotresult to callDataProcessPlots?
| #' method, reference-channel normalization). | ||
| #' @noRd | ||
| qc_show_tmt <- function(dda_dia, bio) { | ||
| isTRUE(dda_dia == "TMT") || (isTRUE(bio == "PTM") && isTRUE(dda_dia == "TMT")) |
There was a problem hiding this comment.
This can simply be isTRUE(dda_dia == "TMT") since the right hand side doesn't add anything additional.
| #' censoring, imputation, summary method). | ||
| #' @noRd | ||
| qc_show_lf <- function(dda_dia, bio) { | ||
| isTRUE(dda_dia == "LType") || (isTRUE(bio == "PTM") && !isTRUE(dda_dia == "TMT")) |
There was a problem hiding this comment.
This can be simplified to only isTRUE(dda_dia == "LType").
| #' Show the top-N feature-count slider: label-free branch with the topN subset selected. | ||
| #' @noRd | ||
| qc_show_features_topn <- function(dda_dia, bio, features_used) { | ||
| qc_show_lf(dda_dia, bio) && isTRUE(features_used == "topN") |
There was a problem hiding this comment.
it should be sufficient to say isTRUE(features_used == "topN")
| #' Show the global-standards picker: label-free non-PTM with globalStandards normalization. | ||
| #' @noRd | ||
| qc_show_standards <- function(norm, bio, dda_dia) { | ||
| isTRUE(norm == "globalStandards") && |
There was a problem hiding this comment.
It should be sufficient to be isTRUE(norm == "globalStandards")
| shinyjs::hide(NAMESPACE_QC$censoring_section) | ||
| shinyjs::hide(NAMESPACE_QC$standards_type_section) | ||
| updateSelectInput(session, NAMESPACE_QC$norm, | ||
| choices = c("none" = "FALSE", "global standards" = "globalStandards"), | ||
| selected = "FALSE") | ||
| updateRadioButtons(session, NAMESPACE_QC$features_used, | ||
| choices = c("Use all features" = "all"), | ||
| selected = "all") |
There was a problem hiding this comment.
Could this code follow the same hiding/showing logic as seen from lines 169 to 176 with the log section?
| }) | ||
|
|
||
| observe ({ | ||
| toggleState("n_feat", input$all_feat == FALSE) |
There was a problem hiding this comment.
Could you check if all_feat is used anywhere? if not, this can be deleted.
| # Enable the maximum-censored-quantile numeric only when its "do not apply | ||
| # cutoff" checkbox is unchecked. | ||
| observe ({ | ||
| shinyjs::toggleState("maxQC", input$null == FALSE) |
There was a problem hiding this comment.
input$null is really ambiguous. Could you rename to something that is easier to read?
| ## Old code for only 20 features. Meena thought this should be all uniques | ||
| ## TODO: Need to fix this bc hard to be specific with slider. | ||
| # if (nrow(unique(get_data()[1])) < 20) { | ||
| # m_feat = nrow(unique(get_data()[1])) | ||
| # } | ||
| # else | ||
| # { | ||
| # m_feat = 20 | ||
| # } |
There was a problem hiding this comment.
You can remove this code.
| m_feat = nrow(unique(get_data()$PTM[1])) | ||
| } else { | ||
| m_feat = nrow(unique(get_data()[1])) |
There was a problem hiding this comment.
m_feat is ideally determined by finding the number of unique features per protein and taking the maximum among all proteins. Here, a feature is a concatenation of PeptideSequence, PrecursorCharge, FragmentIon, and ProductCharge
If it's too complicated, I'd hard-code this to 100 for now, the current logic doesn't make sense as it takes the number of unique proteins.
| radioButtons( | ||
| ns("summaryMethod"), | ||
| label = h4( | ||
| "6. Summarization", |
There was a problem hiding this comment.
Would you be able to remove the numbering here? Plus in other pieces of the sidebar too?
| register_qc_summary <- function(input, output, session, loadpage_input, | ||
| preprocess_data, app_template) { | ||
|
|
||
| abundant = reactiveValues() |
There was a problem hiding this comment.
Can you change this variable to a name that is easier to read through? It gets really confusing seeing terms like abundance and abundant in the same file. Maybe something like abundance_table_reactive_values?
| register_qc_turnover <- function(input, output, session, app_template, get_data, | ||
| get_condition_metadata, preprocess_data) { | ||
|
|
||
| output$turnover_ratios_sidebar <- renderUI({ |
There was a problem hiding this comment.
I'd call this tracer_constants_sidebar
Motivation and context
The QC page had grown into a large, monolithic Shiny UI/server implementation with complex branching across QC workflows—especially PTM vs non-PTM and template-specific behavior (e.g., protein turnover). This made it hard to keep IDs consistent, reason about conditional rendering, and safely extend the QC flow without regressions.
Solution summary
The refactor modularizes the QC page into smaller “registration” helpers (sidebar visibility/options, plots, summarized results, downloads, and turnover ratios) and centralizes QC-related UI/server identifiers via a new
NAMESPACE_QCconstant. The UI shifts from extensiveconditionalPanel(...)logic toshinyjs::hidden(div(...))containers keyed by those centralized namespace IDs, while the server orchestration is streamlined around a “run → next step → switch tab” flow and delegates to the new registration helpers.Detailed changes
Centralized QC namespaces/constants
NAMESPACE_QCinR/constants.Rto map QC input/container IDs (sidebar processing options, summarized-results/plot/download tab IDs, and server-toggled visibility container IDs).NAMESPACE_EXPDESby removingresult_plot = "result_plot".QC server orchestration refactor
R/module-qc-server.R, replaced a large inline block of QC rendering/wiring (caption/proceed navigation, plots/statistics/quantification UI, CSV/plot downloads, and turnover-ratio sidebar + turnover table/download) with a streamlined flow:input$runoutput$submit.button, enabling it aroundpreprocess_data()completionproceed6switches the parent tabset to"StatsModel"viaonclick(...)register_qc_visibility_observers,register_qc_sidebar_options,register_qc_plots,register_qc_summary,register_qc_downloads,register_qc_turnover(capturingturnover_ratiosfrom the turnover registration)QC UI conditional rendering refactor
R/module-qc-ui.R, replacedconditionalPanel(...)-based conditional rendering withshinyjs::hidden(div(...))wrappers.ns(NAMESPACE_QC$...)identifiers, covering:conditionalPanel(...)branches).New QC sidebar predicates + registration
R/qc-server-sidebar.R, added predicate helpers that decide whether specific QC sidebar panels should be shown, including:qc_show_tmt,qc_show_lf)qc_show_maxqc_msstats)qc_show_standards)qc_show_features_topn)qc_show_mbi)qc_show_log_section)qc_show_profileplot_options,qc_show_qualitymetrics_options)qc_show_nonptm_downloads,qc_show_ptm_downloads)register_qc_visibility_observers(...):shinyjs::toggle()to show/hide them via predicatesprotein_turnovertemplate to update the UI flow, toggle template-specific sections, and update normalization/feature-subset controlsshinyjs::toggleState()formaxQC/n_featbased on null/all-features settingsregister_qc_sidebar_options(...)to dynamically render:get_data())summaryMethodradio selector defaulting toTMPand optionally addingMSstats+when anomaly scores are enabledNew QC plots registration
R/qc-server-plots.R, addedregister_qc_plots(...)to register the QC “Summarization Plots” tab:QualityMetricsPlotusing metric columns discovered fromget_data()plotresult()routing to the appropriate plotting function based on PTM/TMT modestatisticsreactive to compute per-condition summary stats frompreprocess_data()$ProteinLevelDataNew QC summarized-results registration
R/qc-server-summary.R, addedregister_qc_summary(...):abundant$resultsreset whenloadpage_input()$proceed1fireseventReactive(abundance) afterpreprocess_data()completionNew QC downloads registration
R/qc-server-downloads.R, addedregister_qc_downloads(...):loadpage_input()$BIO == "PTM", otherwise enables the non-PTM handlerspreprocess_data()sub-tables (feature/protein; PTM variants vs protein-side PTM variants)New QC turnover ratios module
R/qc-server-turnover.R, addedregister_qc_turnover(...):protein_turnovertemplate) and dynamically generated per-condition tracer constant inputs (0–1)input$run, computes turnover ratios viacalculateTurnoverRatioswithnormalize_tracer = TRUEand defaults missing tracer constants to1.0Unit tests
QC UI rendering and namespace correctness (
tests/testthat/test-module-qc-ui.R)qcUI("test")returns atagListidattributes tied toNAMESPACE_QCid="test-norm") collapses to a single instance and contains expected option strings (equalizeMedians,quantile,globalStandards)QC server sidebar predicate behavior and
NAMESPACE_QCID stability (tests/testthat/test-qc-server-rendering.R)qc_show_*predicate helpers across key driver combinations (TMT vs label-free, summarization requirements, standards/features/censoring/log-section gating)type1NAMESPACE_QC(to prevent QC ID renames)Coding guidelines violated
R/qc-server-plots.R, thestatisticsreactive that computes CI-width referencestab$CIeven thoughtab$CIis not clearly defined in the described function logic, which can lead to incorrect CI calculations or runtime errors.