Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
9cf0c56
fix: move required Shiny app packages from Suggests to Imports (#1207)
Gero1999 Apr 9, 2026
7c6d173
chore: drop later from Imports, it is a transitive dep of shiny
Gero1999 Apr 9, 2026
071c205
refactor lintr
Gero1999 Apr 10, 2026
d64e1f4
fix: guard sessionInfo() in About tab against broken packages
Gero1999 Apr 10, 2026
a630c83
chore: add manual test script for verifying app without Suggests
Gero1999 Apr 10, 2026
995f857
Update PR template with dependency checklist
Gero1999 Apr 10, 2026
cecf34c
docs: add dependency test step to PR checklist
Gero1999 Apr 10, 2026
18f63da
fix: add importFrom directives for shiny-only Imports packages
Gero1999 Apr 10, 2026
4ac9ff9
Merge remote-tracking branch 'origin/main' into 1207-bug/fix-suggests…
Gero1999 Apr 13, 2026
1a27f25
Merge branch '1207-bug/fix-suggests-imports-classification' of https:…
Gero1999 Apr 13, 2026
abdb064
make plain script to recover pkgs hidden
Gero1999 Apr 14, 2026
46a92e6
fix: guard sessionInfo() in About tab against missing packages
Gero1999 Apr 17, 2026
bc0c108
fix: guard .export_session_info() in ZIP export against missing packages
Gero1999 Apr 17, 2026
64b0d1e
refactor lintr
Gero1999 Apr 20, 2026
7ca8606
Bump version from 0.1.0.9147 to 0.1.0.9153
Gero1999 Apr 22, 2026
304337a
Merge branch 'main' into 1207-bug/fix-suggests-imports-classification
Gero1999 Apr 22, 2026
06373d5
Bump version from 0.1.0.9153 to 0.1.0.9154
Gero1999 Apr 23, 2026
18fece5
Merge branch 'main' into 1207-bug/fix-suggests-imports-classification
Gero1999 Apr 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ How to test features not covered by unit tests.
- [ ] 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`
- [ ] If a package dependency was added/changed, run `data-raw/test_suggests_hidden.R`

## Notes to reviewer

Expand Down
28 changes: 14 additions & 14 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,34 @@ Description: An interactive 'shiny' application for performing non-compartmental
Noncompartmental Analysis: The PKNCA R Package" <doi:10.1007/s10928-015-9432-2>.
License: Apache License (>= 2)
Imports:
bslib,
dplyr,
formatters,
ggplot2,
glue,
htmltools,
htmlwidgets,
logger,
magrittr,
PKNCA (>= 0.12.1),
plotly (>= 4.11.0),
purrr,
reactable,
reactable.extras,
rlang,
shiny,
shinycssloaders,
shinyjs,
shinyjqui,
shinyWidgets,
stats,
tern,
tidyr,
units,
utils,
writexl,
yaml
yaml,
zip
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.3
Expand All @@ -52,37 +64,25 @@ Suggests:
flextable,
ggh4x,
haven,
htmltools,
htmlwidgets,
jsonlite,
knitr,
lintr (>= 3.2.0),
logger,
markdown,
mockery,
nestcolor,
officer,
pak,
readxl,
reactable,
reactable.extras,
rlistings,
rmarkdown,
sass,
scales,
shiny,
shinycssloaders,
shinyjs,
shinyjqui,
shinytest2,
shinyWidgets,
stringi,
testthat (>= 3.0.0),
tools,
quarto,
vdiffr,
withr,
zip
withr
Config/testthat/edition: 3
Language: en-US
URL: https://pharmaverse.github.io/aNCA/, https://github.com/pharmaverse/aNCA
Expand Down
12 changes: 12 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,20 @@ importFrom(units,deparse_unit)
importFrom(units,drop_units)
importFrom(units,set_units)
importFrom(units,units_options)
importFrom(bslib,page_sidebar)
importFrom(htmltools,tags)
importFrom(htmlwidgets,onRender)
importFrom(logger,log_info)
importFrom(reactable,reactable)
importFrom(reactable.extras,reactable_extras_dependency)
importFrom(shiny,runApp)
importFrom(shinycssloaders,withSpinner)
importFrom(shinyWidgets,pickerInput)
importFrom(shinyjs,useShinyjs)
importFrom(shinyjqui,orderInput)
importFrom(utils,capture.output)
importFrom(utils,read.csv)
importFrom(writexl,write_xlsx)
importFrom(yaml,read_yaml)
importFrom(yaml,write_yaml)
importFrom(zip,zipr)
19 changes: 19 additions & 0 deletions R/imports-shiny.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Declare Imports used only by the Shiny app (inst/shiny/).
#
# R CMD check requires that every package in Imports has at least one
# @importFrom directive. These packages are used in the Shiny app code
# (inst/shiny/) which R CMD check does not scan for namespace usage.

#' @importFrom bslib page_sidebar
#' @importFrom htmltools tags
#' @importFrom htmlwidgets onRender
#' @importFrom logger log_info
#' @importFrom reactable reactable
#' @importFrom reactable.extras reactable_extras_dependency
#' @importFrom shiny runApp
#' @importFrom shinycssloaders withSpinner
#' @importFrom shinyjs useShinyjs
#' @importFrom shinyjqui orderInput
#' @importFrom shinyWidgets pickerInput
#' @importFrom zip zipr
NULL
49 changes: 15 additions & 34 deletions R/run_app.R
Original file line number Diff line number Diff line change
Expand Up @@ -86,43 +86,24 @@ run_app <- function(datapath = NULL, settings = NULL,
}

#' Check if all dependencies required to run shiny application are installed.
#' If not, install them.
#' This list of packages should also be provided as `Suggests` in the DESCRIPTION file.
#'
#' Reads the Imports field from DESCRIPTION and verifies each package is
#' available. This keeps the check in sync with DESCRIPTION automatically.
#' @noRd
check_app_dependencies <- function() {
deps <- c(
"bslib",
"dplyr",
"htmlwidgets",
"logger",
"formatters",
"magrittr",
"plotly",
"purrr",
"reactable",
"reactable.extras",
"shiny",
"shinycssloaders",
"shinyjs",
"shinyjqui",
"shinyWidgets",
"stats",
"stringi",
"tidyr",
"tools",
"utils",
"rlang",
"yaml"
)
desc <- read.dcf(system.file("DESCRIPTION", package = "aNCA"), fields = "Imports")
deps <- trimws(unlist(strsplit(desc[1, "Imports"], ",")))
deps <- gsub("\\s*\\(.*\\)", "", deps) # strip version constraints
deps <- deps[nzchar(deps)]

missing_packages <- purrr::keep(deps, function(dep) !requireNamespace(dep, quietly = TRUE))
missing_packages <- deps[!vapply(deps, requireNamespace, logical(1), quietly = TRUE)]

if (length(missing_packages) != 0) {
stop(paste0(
"Some packages required for Shiny application are missing. ",
"You can install them by running `install.packages(c(",
paste0("'", missing_packages, "'", collapse = ", "),
"))`"
))
if (length(missing_packages) > 0) {
stop(
"Some packages required for the Shiny application are missing. ",
"You can install them by running:\n",
" install.packages(c(", paste0("'", missing_packages, "'", collapse = ", "), "))",
call. = FALSE
)
}
}
50 changes: 50 additions & 0 deletions data-raw/test_suggests_hidden.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Manual test: verify run_app() works without Suggests packages
#
# This script hides all Suggests packages (except testthat/covr needed by
# devtools) and launches the app. Use it to verify that:
#
# 1. The app starts without errors.
# 2. Core workflow works (upload → map → NCA → export).
# 3. Optional features (SAS import, PowerPoint export, listings) show
# graceful messages instead of crashing.
#
# Usage:
# 1. Open a fresh R session (Ctrl+Shift+F10 in RStudio).
# 2. source("data-raw/test_suggests_hidden.R")
# 3. Test the app manually.
# 4. Stop the app (Ctrl+C / Esc), then run restore_packages() to unhide.
#
# WARNING: Do NOT run this in CI. It renames package directories on disk.

# --- Hide Suggests packages ---
desc <- read.dcf("DESCRIPTION", fields = "Suggests")
hide <- trimws(unlist(strsplit(desc[1, "Suggests"], ",")))
hide <- gsub("\\s*\\(.*\\)", "", hide)
hide <- hide[nzchar(hide)]

# Keep packages that devtools::load_all() needs
hide <- setdiff(hide, c("testthat", "covr"))

lib <- .libPaths()[1]
hidden <- character(0)

for (pkg in hide) {
from <- file.path(lib, pkg)
if (dir.exists(from)) {
file.rename(from, paste0(from, "_HIDDEN"))
hidden <- c(hidden, pkg)
}
}
cat("Hidden:", paste(hidden, collapse = ", "), "\n")

# --- Load and run ---
devtools::load_all()
run_app()

# --- Restore function (call after stopping the app) ---
for (pkg in hidden) {
to <- file.path(lib, pkg)
from <- paste0(to, "_HIDDEN")
if (dir.exists(from)) file.rename(from, to)
}
Comment thread
js3110 marked this conversation as resolved.
cat("Restored:", paste(hidden, collapse = ", "), "\n")
2 changes: 1 addition & 1 deletion inst/shiny/app.R
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require(shinyjs)
require(shinyjqui)
require(shinyWidgets)
require(stats)
require(stringi)

require(tidyr)
require(tools)
require(utils)
Expand Down
6 changes: 3 additions & 3 deletions inst/shiny/modules/tab_about.R
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,9 @@ tab_about_server <- function(id) {
})

# Session info — computed once per session (doesn't change at runtime)
session_info_text <- paste(
utils::capture.output(utils::sessionInfo()),
collapse = "\n"
session_info_text <- tryCatch(
paste(utils::capture.output(utils::sessionInfo()), collapse = "\n"),
error = function(e) paste("Session info unavailable:", e$message)
)

output$session_info <- renderText({
Expand Down
10 changes: 8 additions & 2 deletions inst/shiny/modules/tab_tlg.R
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,10 @@ tab_tlg_server <- function(id, data) {
panels <- lapply(tlg_order_graphs, function(g_id) {
graph_ui <- {
g_def <- .TLG_DEFINITIONS[[g_id]]
module_id <- paste0(g_id, stringi::stri_rand_strings(1, 5))
module_id <- paste0(
g_id,
paste0(sample(c(letters, 0:9), 5, replace = TRUE), collapse = "")
)

if (exists(g_def$fun)) {
tlg_module_server(module_id, data, "graph", get(g_def$fun), g_def$options)
Expand Down Expand Up @@ -288,7 +291,10 @@ tab_tlg_server <- function(id, data) {
panels <- lapply(tlg_order_listings, function(g_id) {
list_ui <- {
g_def <- .TLG_DEFINITIONS[[g_id]]
module_id <- paste0(g_id, stringi::stri_rand_strings(1, 5))
module_id <- paste0(
g_id,
paste0(sample(c(letters, 0:9), 5, replace = TRUE), collapse = "")
)

if (exists(g_def$fun)) {
tlg_module_server(module_id, data, "listing", get(g_def$fun), g_def$options)
Expand Down
Loading