Skip to content

fix: R script and settings export missing volume unit simplification (#1190)#1228

Open
Gero1999 wants to merge 10 commits intomainfrom
1190-fix/units-table-detect-changes-by-value
Open

fix: R script and settings export missing volume unit simplification (#1190)#1228
Gero1999 wants to merge 10 commits intomainfrom
1190-fix/units-table-detect-changes-by-value

Conversation

@Gero1999
Copy link
Copy Markdown
Collaborator

Issue

Closes #1190

Description

The volume unit simplification applied in tab_data.R (e.g. mg*L/mLmg) was not being captured in the exported settings or R script. This happened because session$userData$units_table() only stored rows that the user explicitly edited in the Units modal — it used a default flag to track which rows were touched, and the automatic simplification never set that flag.

Root cause: The default column (added in #900 / PR #946) tracked edits by flagging rows as default = FALSE when the user clicked on them in the modal. Automatic changes (like the volume simplification) mutated the PKNCAdata object directly without going through the modal, so they were never flagged.

Fix: Replace the default flag with a value-based comparison: rows where PPSTRESU != PPORRESU are treated as changed. This captures both user edits and automatic simplifications, since any change to the display unit results in PPSTRESU differing from PPORRESU.

Changes

  1. Save handler (units_table.R): Filter by PPSTRESU != PPORRESU instead of !default
  2. Modal open (units_table.R): Stop adding default = TRUE/FALSE columns when building the table
  3. Reactable (units_table.R): Remove hidden default column definition
  4. Edit handler (units_table.R): Remove default <- FALSE assignment on edit
  5. Global observer (units_table.R): Remove default from merge logic
  6. Settings export (nca_setup.R, zip-utils.R): Remove filter(!default) %>% select(-default) — the units table already contains only changed rows
  7. NCA run (tab_nca.R): Remove select(-any_of("default")) guard

Definition of Done

  • Volume unit simplification is included in session$userData$units_table()
  • Settings YAML export includes simplified volume units
  • R script receives simplified volume units via custom_units_table
  • User edits in the Units modal still work correctly
  • default column fully removed from the units table flow
  • Manual verification: R script produces same units as the Shiny app

How to test

  1. Upload a dataset that has volume-based parameters (e.g. Vd, Vss)
  2. Run NCA in the Shiny app — note the simplified units in the results
  3. Export the settings YAML — verify it includes the simplified volume units
  4. Export the R script — verify custom_units_table contains the simplified units
  5. Run the R script — verify the output units match the Shiny app
  6. Open the Units modal, change a unit manually, save — verify the change is captured in the settings export
  7. Re-open the Units modal — verify the manual change persists

Contributor checklist

  • Code passes lintr checks
  • Code passes all unit tests
  • New logic covered by unit tests
  • New logic is documented
  • App or package changes are reflected in NEWS
  • Package version is incremented
  • R script works with the new implementation (if applicable)
  • Settings upload works with the new implementation (if applicable)
  • If any .scss change was done, run data-raw/compile_css.R

Notes to reviewer

Gero1999 and others added 9 commits April 15, 2026 13:25
Replace the default flag filter in the save handler with a value-based
comparison. Rows where PPSTRESU differs from PPORRESU are treated as
changed, regardless of whether the change came from a user edit or an
automatic simplification (e.g. volume units).

This ensures the volume unit simplification from tab_data.R is captured
in session$userData$units_table() and subsequently included in the
exported settings and R script.

Refs #1190

Co-authored-by: Ona <no-reply@ona.com>
The modal no longer adds a default column when building the table.
Custom units are merged by matching on all columns except PPSTRESU
and conversion_factor, without needing a tracking flag.

Refs #1190

Co-authored-by: Ona <no-reply@ona.com>
The default column is no longer needed in the reactable definition
(was hidden) or in the edit handler (was set to FALSE on edit).
Change detection is now purely value-based.

Refs #1190

Co-authored-by: Ona <no-reply@ona.com>
The units table stored in session$userData$units_table() now only
contains rows where PPSTRESU != PPORRESU, so the export paths in
nca_setup.R (settings download) and zip-utils.R (ZIP export) no
longer need to filter by the default flag.

Refs #1190

Co-authored-by: Ona <no-reply@ona.com>
The observer that syncs the local modal_units_table with the global
session$userData$units_table no longer uses the default column.
Merging is done by matching on all columns except PPSTRESU and
conversion_factor.

Refs #1190

Co-authored-by: Ona <no-reply@ona.com>
Remove the select(-any_of("default")) guard from tab_nca.R and the
save handler in units_table.R. The default column no longer exists
in the units table, so these guards are unnecessary.

Refs #1190

Co-authored-by: Ona <no-reply@ona.com>
Refs #1190

Co-authored-by: Ona <no-reply@ona.com>
When pknca_data() is created, any units where PPSTRESU differs from
PPORRESU (e.g. volume unit simplification) are automatically stored
in session$userData$units_table(). This ensures the settings export
and R script include these changes even if the user never opens the
Units modal.

Previously, session$userData$units_table() stayed NULL until the
modal was opened and saved, causing settings to export units: ~.

Refs #1190

Co-authored-by: Ona <no-reply@ona.com>
PKNCA can produce units rows where PPORRESU and PPSTRESU are both NA
(parameters with no derivable units). The comparison NA != NA returns
NA, which R treats as truthy in subsetting, causing these rows to leak
into the saved units table as .na.character entries.

Add explicit is.na() guards in both the auto-populate observer and the
modal save handler.

Refs #1190

Co-authored-by: Ona <no-reply@ona.com>
@Gero1999 Gero1999 requested a review from Copilot April 15, 2026 13:54
@Gero1999 Gero1999 marked this pull request as ready for review April 15, 2026 13:55
@Gero1999 Gero1999 requested review from Shaakon35 and js3110 April 15, 2026 13:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes an export mismatch in the Shiny NCA workflow where automatic unit simplifications (not performed via the Units modal) were not reflected in exported settings or generated R scripts.

Changes:

  • Replace the previous edit-tracking default flag with value-based detection of unit changes (PPSTRESU != PPORRESU).
  • Remove default column handling across Units modal plumbing and export paths.
  • Add an observer to auto-populate session$userData$units_table() from pknca_data() when units differ from defaults.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
inst/shiny/modules/tab_nca/units_table.R Removes default flag usage; switches to value-based “changed unit” filtering.
inst/shiny/modules/tab_nca/nca_setup.R Removes export-time filtering/stripping of default from units settings.
inst/shiny/modules/tab_nca.R Auto-populates units_table based on PPSTRESU != PPORRESU; removes default drop guard.
inst/shiny/functions/zip-utils.R Stops filtering units by default during ZIP settings export.
NEWS.md Documents the bug fix and the new change-detection behavior.
DESCRIPTION Bumps package version.
Comments suppressed due to low confidence (3)

inst/shiny/modules/tab_nca/units_table.R:39

  • dplyr::rows_update() can error if custom_units contains columns not present in default_units (e.g., older settings uploads or other sources adding extra cols). Elsewhere the codebase defensively subsets the update table to shared columns before calling rows_update() (see R/PKNCA.R where custom_units_table[, common_cols, drop = FALSE] is used). Consider applying the same pattern here by restricting custom_units to intersect(names(default_units), names(custom_units)) before the update.
      if (!is.null(session$userData$units_table())) {
        custom_units <- session$userData$units_table()
        by_cols <- intersect(names(default_units), names(custom_units))
        by_cols <- setdiff(by_cols, c("PPSTRESU", "conversion_factor"))
        dplyr::rows_update(
          default_units,
          custom_units,
          by = by_cols,
          unmatched = "ignore"

inst/shiny/modules/tab_nca/units_table.R:212

  • Same as above: rows_update() is passed custom_units without trimming to shared columns. To avoid runtime errors when session$userData$units_table() includes extra columns (e.g., from uploaded settings), subset custom_units to the shared column set before updating default_units (consistent with R/PKNCA.R).
      custom_units <- session$userData$units_table()
      by_cols <- intersect(names(default_units), names(custom_units))
      by_cols <- setdiff(by_cols, c("PPSTRESU", "conversion_factor"))
      dplyr::rows_update(
        default_units,
        custom_units,
        by = by_cols,
        unmatched = "ignore"
      ) %>%

inst/shiny/modules/tab_nca.R:160

  • When applying custom_units via rows_update(), consider subsetting custom_units to the shared column set (intersect(names(processed_pknca_data$units), names(custom_units))) before updating. This matches the defensive pattern in R/PKNCA.R and avoids potential errors if session$userData$units_table() ever carries extra columns (e.g., from settings upload).
          custom_units <- session$userData$units_table()
          by_cols <- intersect(names(processed_pknca_data$units), names(custom_units))
          by_cols <- setdiff(by_cols, c("PPSTRESU", "conversion_factor"))
          processed_pknca_data$units <- rows_update(
            processed_pknca_data$units,
            custom_units,
            by = by_cols,
            unmatched = "ignore"

Comment thread inst/shiny/modules/tab_nca.R Outdated
log_trace("Applying custom units specification.")
modal_units_table() %>%
dplyr::filter(!default) %>%
dplyr::filter(!is.na(PPSTRESU), !is.na(PPORRESU), PPSTRESU != PPORRESU) %>%
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

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

The save handler now persists only rows where PPSTRESU != PPORRESU. This drops user changes where only conversion_factor was edited (allowed via editable = c("PPSTRESU", "conversion_factor")) while keeping the unit label unchanged, so those changes won’t be exported/applied. Consider treating a row as changed when either PPSTRESU != PPORRESU OR conversion_factor != 1 (with appropriate NA handling).

Suggested change
dplyr::filter(!is.na(PPSTRESU), !is.na(PPORRESU), PPSTRESU != PPORRESU) %>%
dplyr::filter(
(!is.na(PPSTRESU) & !is.na(PPORRESU) & PPSTRESU != PPORRESU) |
dplyr::coalesce(conversion_factor, 1) != 1
) %>%

Copilot uses AI. Check for mistakes.
…y having diffs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: R script template missing volume unit simplification step

2 participants