From 716f1c533dc7d007f654afd73c2733bcd50f561f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Feb 2026 07:27:25 +0000
Subject: [PATCH 1/9] Initial plan
From c05cb4352a2ae19abb3fcc1ccb0755f1808e7b13 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Feb 2026 07:32:09 +0000
Subject: [PATCH 2/9] Add foodwebr to DESCRIPTION and create package-structure
article
Co-authored-by: d-morrison <2474437+d-morrison@users.noreply.github.com>
---
DESCRIPTION | 1 +
vignettes/articles/package-structure.qmd | 294 +++++++++++++++++++++++
2 files changed, 295 insertions(+)
create mode 100644 vignettes/articles/package-structure.qmd
diff --git a/DESCRIPTION b/DESCRIPTION
index 721e65a5..c4f27852 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -18,6 +18,7 @@ Imports:
stats
Suggests:
altdoc,
+ foodwebr,
knitr,
rmarkdown,
spelling,
diff --git a/vignettes/articles/package-structure.qmd b/vignettes/articles/package-structure.qmd
new file mode 100644
index 00000000..7e32b1fa
--- /dev/null
+++ b/vignettes/articles/package-structure.qmd
@@ -0,0 +1,294 @@
+---
+title: "Package Function Structure"
+author: "UC Davis Seroepidemiology Research Group (UCD-SERG)"
+date: "`r Sys.Date()`"
+format:
+ revealjs:
+ output-file: package-structure-revealjs.html
+ html: default
+ docx: default
+---
+
+```{r}
+#| include: false
+knitr::opts_chunk$set(
+ collapse = TRUE,
+ comment = "#>",
+ fig.width = 7,
+ fig.height = 5
+)
+```
+
+```{r setup}
+#| message: false
+library(rpt)
+```
+
+## Overview
+
+This article demonstrates how to visualize the structure and dependencies of package functions using the [`foodwebr`](https://lewinfox.com/foodwebr/) package. Understanding function dependencies helps developers:
+
+- Navigate and understand codebases more quickly
+- Identify potential refactoring opportunities
+- Document architecture and design decisions
+- Onboard new contributors more effectively
+
+## What is foodwebr?
+
+The `foodwebr` package creates dependency graphs showing which functions call which other functions. These visualizations are particularly useful for:
+
+- Exploring unfamiliar codebases
+- Understanding function relationships
+- Identifying central or isolated functions
+- Planning refactoring efforts
+
+## Installing foodwebr
+
+If you don't have `foodwebr` installed, you can install it from CRAN:
+
+```{r}
+#| eval: false
+install.packages("foodwebr")
+```
+
+Or from GitHub for the latest development version:
+
+```{r}
+#| eval: false
+devtools::install_github("lewinfox/foodwebr")
+```
+
+## Visualizing Package Structure
+
+### Basic Usage
+
+The `foodweb()` function analyzes function dependencies. When examining a package, you typically want to look at specific functions or the entire package namespace:
+
+```{r}
+#| message: false
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+library(foodwebr)
+
+# Create a foodweb for the example_function
+fw <- foodweb(FUN = example_function)
+fw
+```
+
+### Plotting the Dependency Graph
+
+The `plot()` method creates an interactive visualization using DiagrammeR:
+
+```{r}
+#| label: fig-basic-foodweb
+#| fig-cap: "Basic function dependency graph for example_function"
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+plot(fw)
+```
+
+::: {.callout-note}
+## Understanding the Graph
+
+- **Nodes** represent individual functions
+- **Edges** (arrows) show function calls
+- An arrow from A to B means "A calls B"
+- Isolated nodes indicate functions with no dependencies or dependents
+:::
+
+### Filtering Options
+
+By default, `foodwebr` filters to show only functions directly related to the specified function. You can control this behavior:
+
+```{r}
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+# Show only connected functions (default)
+fw_filtered <- foodweb(FUN = example_function, filter = TRUE)
+
+# Show all functions in the environment
+fw_all <- foodweb(FUN = example_function, filter = FALSE)
+```
+
+### Examining the Entire Package
+
+To see all exported and internal functions in the package:
+
+```{r}
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+# Get the package namespace environment
+pkg_env <- asNamespace("rpt")
+
+# Create foodweb for entire package
+fw_pkg <- foodweb(env = pkg_env)
+fw_pkg
+```
+
+```{r}
+#| label: fig-package-foodweb
+#| fig-cap: "Complete dependency graph for all package functions"
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+plot(fw_pkg)
+```
+
+## Analyzing Dependencies
+
+### Understanding Function Roles
+
+From the dependency graph, you can identify different types of functions:
+
+- **Leaf functions**: Functions that don't call any other package functions (only base R or external packages)
+- **Root functions**: Functions that are called by many others but don't call package functions themselves
+- **Hub functions**: Functions that both call and are called by many functions
+- **Isolated functions**: Functions with no connections to others
+
+### Example Analysis
+
+For our package:
+
+```{r}
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+# Convert to text representation for inspection
+cat(as.character(fw_pkg))
+```
+
+::: {.callout-tip}
+## Dependency Patterns
+
+In well-designed packages, you typically see:
+
+1. **Clear hierarchy**: Lower-level utility functions are called by higher-level API functions
+2. **Limited cross-dependencies**: Functions in the same "layer" don't call each other excessively
+3. **Focused functions**: Each function has a clear purpose with limited dependencies
+:::
+
+## Advanced Usage
+
+### Using with tidygraph
+
+The `tidygraph` package provides powerful tools for graph analysis. You can convert a `foodweb` object to work with these tools:
+
+```{r}
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE) && requireNamespace("tidygraph", quietly = TRUE)
+if (requireNamespace("tidygraph", quietly = TRUE)) {
+ tg <- tidygraph::as_tbl_graph(fw_pkg)
+ print(tg)
+}
+```
+
+### Customizing Visualizations
+
+You can pass additional arguments to customize the graph appearance:
+
+```{r}
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+#| eval: false
+# These arguments are passed to DiagrammeR::grViz()
+plot(fw_pkg,
+ width = 800,
+ height = 600)
+```
+
+### Exporting as Text
+
+Get the graphviz DOT representation:
+
+```{r}
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+# As text
+foodweb_text <- foodweb(env = asNamespace("rpt"), as.text = TRUE)
+cat(foodweb_text)
+```
+
+## Practical Applications
+
+### Code Review
+
+Use dependency graphs during code review to:
+
+- Verify that new functions follow existing architectural patterns
+- Identify unexpected dependencies
+- Ensure proper separation of concerns
+- Detect circular dependencies
+
+### Refactoring
+
+Before refactoring:
+
+1. Generate a dependency graph of the affected area
+2. Identify all functions that will be impacted
+3. Plan changes to minimize ripple effects
+4. After refactoring, regenerate the graph to verify improvements
+
+### Documentation
+
+Include dependency graphs in:
+
+- Developer documentation
+- Architecture decision records
+- Package vignettes (like this one!)
+- README files for complex packages
+
+::: {.callout-important}
+## Keeping Documentation Updated
+
+When adding new functions or modifying dependencies, regenerate the foodweb graphs to keep documentation current. Consider adding automated checks in your CI/CD pipeline.
+:::
+
+## Example: Growing Package
+
+As this package grows, the dependency graph will become more interesting. Here's a hypothetical example of what it might look like with additional functions:
+
+::: {.callout-note collapse="true"}
+## Hypothetical Example
+
+Imagine we add these functions:
+
+- `validate_input()` - Input validation helper
+- `calculate_statistic()` - Main calculation function
+- `format_output()` - Output formatting function
+- `example_function()` - High-level API function (existing)
+
+With `example_function()` calling `validate_input()`, `calculate_statistic()`, and `format_output()`, and `calculate_statistic()` calling `stats::median()`, the dependency graph would show:
+
+```
+example_function() -> { validate_input(), calculate_statistic(), format_output() }
+calculate_statistic() -> { stats::median() }
+validate_input()
+format_output()
+```
+
+This would clearly show that `example_function()` is the main entry point, with well-separated concerns for validation, calculation, and formatting.
+:::
+
+## Best Practices
+
+When designing package architecture:
+
+1. **Minimize dependencies**: Each function should have a clear, focused purpose
+2. **Avoid circular dependencies**: A should not call B if B calls A
+3. **Layer your functions**: Separate low-level utilities from high-level APIs
+4. **Document dependencies**: Use `foodwebr` graphs in your documentation
+5. **Review regularly**: Check dependency graphs during code review
+
+## Learn More
+
+- **foodwebr documentation**:
+- **Package website**:
+- **GitHub repository**:
+- **DiagrammeR**:
+- **tidygraph**:
+
+## Summary
+
+This article demonstrated how to use `foodwebr` to visualize and understand package function dependencies. Key takeaways:
+
+- `foodwebr` creates interactive dependency graphs
+- Filtering options help focus on specific functions or show entire packages
+- Dependency graphs aid in code review, refactoring, and documentation
+- Integration with `tidygraph` enables advanced graph analysis
+- Regular visualization helps maintain clean architecture
+
+As the `rpt` package grows, these visualization tools will become increasingly valuable for understanding and maintaining the codebase.
+
+## References
+
+::: {#refs}
+:::
From 884da647a9d4da3f94ec2b2a28a10bd820bae22c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Feb 2026 07:38:11 +0000
Subject: [PATCH 3/9] Add helper functions with dependencies for foodwebr demo
and update tests
Co-authored-by: d-morrison <2474437+d-morrison@users.noreply.github.com>
---
NAMESPACE | 1 +
R/calculate_statistic.R | 13 ++++
R/calculate_summary.R | 21 ++++++
R/clean_data.R | 18 +++++
R/example_function.R | 7 +-
R/format_result.R | 12 ++++
R/validate_input.R | 21 ++++++
man/calculate_summary.Rd | 21 ++++++
man/example_function.Rd | 4 +-
tests/testthat/test-calculate_summary.R | 21 ++++++
tests/testthat/test-example_function.R | 8 +++
vignettes/articles/package-structure.qmd | 86 ++++++++++++++++++------
12 files changed, 211 insertions(+), 22 deletions(-)
create mode 100644 R/calculate_statistic.R
create mode 100644 R/calculate_summary.R
create mode 100644 R/clean_data.R
create mode 100644 R/format_result.R
create mode 100644 R/validate_input.R
create mode 100644 man/calculate_summary.Rd
create mode 100644 tests/testthat/test-calculate_summary.R
diff --git a/NAMESPACE b/NAMESPACE
index 79e14c4f..93b6af16 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -1,3 +1,4 @@
# Generated by roxygen2: do not edit by hand
+export(calculate_summary)
export(example_function)
diff --git a/R/calculate_statistic.R b/R/calculate_statistic.R
new file mode 100644
index 00000000..d882d062
--- /dev/null
+++ b/R/calculate_statistic.R
@@ -0,0 +1,13 @@
+#' Calculate Statistic
+#'
+#' Internal helper function to calculate a statistical measure on cleaned data.
+#'
+#' @param x A numeric vector
+#'
+#' @return The median value
+#' @keywords internal
+#' @noRd
+calculate_statistic <- function(x) {
+ x_clean <- clean_data(x)
+ stats::median(x_clean)
+}
diff --git a/R/calculate_summary.R b/R/calculate_summary.R
new file mode 100644
index 00000000..ab9357be
--- /dev/null
+++ b/R/calculate_summary.R
@@ -0,0 +1,21 @@
+#' Calculate Summary Statistics
+#'
+#' Calculate multiple summary statistics for a numeric vector.
+#'
+#' @param x A numeric vector
+#'
+#' @return A named list with mean, median, and standard deviation
+#' @export
+#'
+#' @examples
+#' calculate_summary(c(1, 2, 3, 4, 5))
+#' calculate_summary(c(1, NA, 3, 4, 5))
+calculate_summary <- function(x) {
+ x_clean <- clean_data(x)
+
+ list(
+ mean = format_result(mean(x_clean)),
+ median = example_function(x),
+ sd = format_result(stats::sd(x_clean))
+ )
+}
diff --git a/R/clean_data.R b/R/clean_data.R
new file mode 100644
index 00000000..9b34e189
--- /dev/null
+++ b/R/clean_data.R
@@ -0,0 +1,18 @@
+#' Clean Data
+#'
+#' Internal helper function to clean data by removing NA values and preparing
+#' for analysis.
+#'
+#' @param x A numeric vector
+#'
+#' @return A cleaned numeric vector with NA values removed
+#' @keywords internal
+#' @noRd
+clean_data <- function(x) {
+ validate_input(x)
+ x_clean <- x[!is.na(x)]
+ if (length(x_clean) == 0) {
+ stop("No valid data remaining after removing NA values", call. = FALSE)
+ }
+ x_clean
+}
diff --git a/R/example_function.R b/R/example_function.R
index 962aade8..5d95050f 100644
--- a/R/example_function.R
+++ b/R/example_function.R
@@ -1,14 +1,17 @@
#' Example Function
#'
#' This is an example function that demonstrates basic functionality.
+#' It validates, cleans, calculates statistics, and formats the result.
#'
#' @param x A numeric vector
#'
-#' @return The median of the input vector
+#' @return The median of the input vector, rounded to 2 decimal places
#' @export
#'
#' @examples
#' example_function(c(1, 2, 3, 4, 5))
+#' example_function(c(1, NA, 3, 4, 5))
example_function <- function(x) {
- stats::median(x)
+ result <- calculate_statistic(x)
+ format_result(result)
}
diff --git a/R/format_result.R b/R/format_result.R
new file mode 100644
index 00000000..95c246ec
--- /dev/null
+++ b/R/format_result.R
@@ -0,0 +1,12 @@
+#' Format Result
+#'
+#' Internal helper function to format the result for output.
+#'
+#' @param result A numeric value
+#'
+#' @return A formatted numeric value (rounded to 2 decimal places)
+#' @keywords internal
+#' @noRd
+format_result <- function(result) {
+ round(result, digits = 2)
+}
diff --git a/R/validate_input.R b/R/validate_input.R
new file mode 100644
index 00000000..cb4cf91d
--- /dev/null
+++ b/R/validate_input.R
@@ -0,0 +1,21 @@
+#' Validate Input Data
+#'
+#' Internal helper function to validate input data for statistical calculations.
+#'
+#' @param x A numeric vector
+#'
+#' @return TRUE if valid, throws error otherwise
+#' @keywords internal
+#' @noRd
+validate_input <- function(x) {
+ if (!is.numeric(x)) {
+ stop("Input must be numeric", call. = FALSE)
+ }
+ if (length(x) == 0) {
+ stop("Input must have at least one element", call. = FALSE)
+ }
+ if (all(is.na(x))) {
+ stop("Input cannot be all NA values", call. = FALSE)
+ }
+ TRUE
+}
diff --git a/man/calculate_summary.Rd b/man/calculate_summary.Rd
new file mode 100644
index 00000000..2dd13384
--- /dev/null
+++ b/man/calculate_summary.Rd
@@ -0,0 +1,21 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/calculate_summary.R
+\name{calculate_summary}
+\alias{calculate_summary}
+\title{Calculate Summary Statistics}
+\usage{
+calculate_summary(x)
+}
+\arguments{
+\item{x}{A numeric vector}
+}
+\value{
+A named list with mean, median, and standard deviation
+}
+\description{
+Calculate multiple summary statistics for a numeric vector.
+}
+\examples{
+calculate_summary(c(1, 2, 3, 4, 5))
+calculate_summary(c(1, NA, 3, 4, 5))
+}
diff --git a/man/example_function.Rd b/man/example_function.Rd
index 497dea06..74d7e12e 100644
--- a/man/example_function.Rd
+++ b/man/example_function.Rd
@@ -10,11 +10,13 @@ example_function(x)
\item{x}{A numeric vector}
}
\value{
-The median of the input vector
+The median of the input vector, rounded to 2 decimal places
}
\description{
This is an example function that demonstrates basic functionality.
+It validates, cleans, calculates statistics, and formats the result.
}
\examples{
example_function(c(1, 2, 3, 4, 5))
+example_function(c(1, NA, 3, 4, 5))
}
diff --git a/tests/testthat/test-calculate_summary.R b/tests/testthat/test-calculate_summary.R
new file mode 100644
index 00000000..aa648da2
--- /dev/null
+++ b/tests/testthat/test-calculate_summary.R
@@ -0,0 +1,21 @@
+test_that("calculate_summary works", {
+ result <- calculate_summary(c(1, 2, 3, 4, 5))
+ expect_type(result, "list")
+ expect_named(result, c("mean", "median", "sd"))
+ expect_equal(result$mean, 3)
+ expect_equal(result$median, 3)
+ expect_equal(result$sd, 1.58)
+})
+
+test_that("calculate_summary handles NA values", {
+ result <- calculate_summary(c(1, NA, 3, 4, 5))
+ expect_type(result, "list")
+ expect_equal(result$median, 3.5)
+ expect_equal(result$mean, 3.25)
+})
+
+test_that("calculate_summary handles errors", {
+ expect_error(calculate_summary(character()), "Input must be numeric")
+ expect_error(calculate_summary(numeric()), "Input must have at least one element")
+ expect_error(calculate_summary(c(NA_real_, NA_real_, NA_real_)), "Input cannot be all NA values")
+})
diff --git a/tests/testthat/test-example_function.R b/tests/testthat/test-example_function.R
index f86a3be1..c77dbc7a 100644
--- a/tests/testthat/test-example_function.R
+++ b/tests/testthat/test-example_function.R
@@ -1,4 +1,12 @@
test_that("example_function works", {
expect_equal(example_function(c(1, 2, 3)), 2)
expect_equal(example_function(c(1, 2, 3, 4, 5)), 3)
+ expect_equal(example_function(c(1, NA, 3, 4, 5)), 3.5)
+ expect_equal(example_function(c(1.111, 2.222, 3.333)), 2.22)
+})
+
+test_that("example_function handles errors", {
+ expect_error(example_function(character()), "Input must be numeric")
+ expect_error(example_function(numeric()), "Input must have at least one element")
+ expect_error(example_function(c(NA_real_, NA_real_, NA_real_)), "Input cannot be all NA values")
})
diff --git a/vignettes/articles/package-structure.qmd b/vignettes/articles/package-structure.qmd
index 7e32b1fa..0f6d51c3 100644
--- a/vignettes/articles/package-structure.qmd
+++ b/vignettes/articles/package-structure.qmd
@@ -157,8 +157,47 @@ In well-designed packages, you typically see:
1. **Clear hierarchy**: Lower-level utility functions are called by higher-level API functions
2. **Limited cross-dependencies**: Functions in the same "layer" don't call each other excessively
3. **Focused functions**: Each function has a clear purpose with limited dependencies
+
+In the `rpt` package, we can see this pattern:
+
+- `validate_input()` is a low-level helper (no package dependencies)
+- `clean_data()` calls `validate_input()`
+- `calculate_statistic()` calls `clean_data()`
+- `format_result()` is a low-level formatter (no package dependencies)
+- `example_function()` is a high-level API that calls `calculate_statistic()` and `format_result()`
+- `calculate_summary()` is another high-level API that calls `clean_data()` and `example_function()`
+
+This creates a clear layered architecture with well-separated concerns.
:::
+## Package Architecture
+
+### Current Structure
+
+Let's examine the actual structure of the `rpt` package:
+
+```{r}
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+# List all functions in the package
+pkg_functions <- ls(asNamespace("rpt"))
+cat("Package functions:\n")
+cat(paste("-", sort(pkg_functions), collapse = "\n"))
+```
+
+The dependency relationships are:
+
+- **Exported functions** (user-facing API):
+ - `example_function()`: Calls `calculate_statistic()` → `format_result()`
+ - `calculate_summary()`: Calls `clean_data()` → `example_function()`
+
+- **Internal functions** (implementation details):
+ - `validate_input()`: No package dependencies (leaf function)
+ - `clean_data()`: Calls `validate_input()`
+ - `calculate_statistic()`: Calls `clean_data()`
+ - `format_result()`: No package dependencies (leaf function)
+
+This architecture demonstrates good separation of concerns with clear data flow from validation through cleaning to calculation and formatting.
+
## Advanced Usage
### Using with tidygraph
@@ -232,31 +271,35 @@ Include dependency graphs in:
When adding new functions or modifying dependencies, regenerate the foodweb graphs to keep documentation current. Consider adding automated checks in your CI/CD pipeline.
:::
-## Example: Growing Package
-
-As this package grows, the dependency graph will become more interesting. Here's a hypothetical example of what it might look like with additional functions:
+## Exploring Specific Functions
-::: {.callout-note collapse="true"}
-## Hypothetical Example
+You can focus on specific functions to understand their dependencies:
-Imagine we add these functions:
+### Example Function Dependencies
-- `validate_input()` - Input validation helper
-- `calculate_statistic()` - Main calculation function
-- `format_output()` - Output formatting function
-- `example_function()` - High-level API function (existing)
+```{r}
+#| label: fig-example-function
+#| fig-cap: "Dependency graph for example_function showing all related functions"
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+fw_example <- foodweb(FUN = example_function)
+plot(fw_example)
+```
-With `example_function()` calling `validate_input()`, `calculate_statistic()`, and `format_output()`, and `calculate_statistic()` calling `stats::median()`, the dependency graph would show:
+### Summary Function Dependencies
-```
-example_function() -> { validate_input(), calculate_statistic(), format_output() }
-calculate_statistic() -> { stats::median() }
-validate_input()
-format_output()
+```{r}
+#| label: fig-summary-function
+#| fig-cap: "Dependency graph for calculate_summary showing its relationships"
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+fw_summary <- foodweb(FUN = calculate_summary)
+plot(fw_summary)
```
-This would clearly show that `example_function()` is the main entry point, with well-separated concerns for validation, calculation, and formatting.
-:::
+These focused views help understand:
+
+- What functions a specific function depends on (its dependencies)
+- What functions depend on a specific function (its dependents)
+- The complete call chain for a given function
## Best Practices
@@ -286,7 +329,12 @@ This article demonstrated how to use `foodwebr` to visualize and understand pack
- Integration with `tidygraph` enables advanced graph analysis
- Regular visualization helps maintain clean architecture
-As the `rpt` package grows, these visualization tools will become increasingly valuable for understanding and maintaining the codebase.
+The `rpt` package demonstrates a clean layered architecture with:
+- Low-level utility functions (`validate_input`, `format_result`)
+- Mid-level processing functions (`clean_data`, `calculate_statistic`)
+- High-level API functions (`example_function`, `calculate_summary`)
+
+This structure makes the code easier to understand, test, and maintain.
## References
From c1c9d5c0e71240d9d50a5825be0e95c3d0b667d7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Feb 2026 07:42:09 +0000
Subject: [PATCH 4/9] Fix YAML error and validate documentation build
Co-authored-by: d-morrison <2474437+d-morrison@users.noreply.github.com>
---
tests/testthat/test-calculate_summary.R | 10 ++++++++--
tests/testthat/test-example_function.R | 10 ++++++++--
vignettes/articles/package-structure.qmd | 1 -
3 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/tests/testthat/test-calculate_summary.R b/tests/testthat/test-calculate_summary.R
index aa648da2..325ba056 100644
--- a/tests/testthat/test-calculate_summary.R
+++ b/tests/testthat/test-calculate_summary.R
@@ -16,6 +16,12 @@ test_that("calculate_summary handles NA values", {
test_that("calculate_summary handles errors", {
expect_error(calculate_summary(character()), "Input must be numeric")
- expect_error(calculate_summary(numeric()), "Input must have at least one element")
- expect_error(calculate_summary(c(NA_real_, NA_real_, NA_real_)), "Input cannot be all NA values")
+ expect_error(
+ calculate_summary(numeric()),
+ "Input must have at least one element"
+ )
+ expect_error(
+ calculate_summary(c(NA_real_, NA_real_, NA_real_)),
+ "Input cannot be all NA values"
+ )
})
diff --git a/tests/testthat/test-example_function.R b/tests/testthat/test-example_function.R
index c77dbc7a..d7d98a3b 100644
--- a/tests/testthat/test-example_function.R
+++ b/tests/testthat/test-example_function.R
@@ -7,6 +7,12 @@ test_that("example_function works", {
test_that("example_function handles errors", {
expect_error(example_function(character()), "Input must be numeric")
- expect_error(example_function(numeric()), "Input must have at least one element")
- expect_error(example_function(c(NA_real_, NA_real_, NA_real_)), "Input cannot be all NA values")
+ expect_error(
+ example_function(numeric()),
+ "Input must have at least one element"
+ )
+ expect_error(
+ example_function(c(NA_real_, NA_real_, NA_real_)),
+ "Input cannot be all NA values"
+ )
})
diff --git a/vignettes/articles/package-structure.qmd b/vignettes/articles/package-structure.qmd
index 0f6d51c3..c9bb6079 100644
--- a/vignettes/articles/package-structure.qmd
+++ b/vignettes/articles/package-structure.qmd
@@ -217,7 +217,6 @@ if (requireNamespace("tidygraph", quietly = TRUE)) {
You can pass additional arguments to customize the graph appearance:
```{r}
-#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
#| eval: false
# These arguments are passed to DiagrammeR::grViz()
plot(fw_pkg,
From 6fef1bd46a3485ef2d83ab6b188c0d9937baa7f3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Feb 2026 07:45:36 +0000
Subject: [PATCH 5/9] Address code review feedback: fix formatting and remove
redundant validation
Co-authored-by: d-morrison <2474437+d-morrison@users.noreply.github.com>
---
R/clean_data.R | 6 +-----
vignettes/articles/package-structure.qmd | 6 +++---
2 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/R/clean_data.R b/R/clean_data.R
index 9b34e189..7bd4d3fc 100644
--- a/R/clean_data.R
+++ b/R/clean_data.R
@@ -10,9 +10,5 @@
#' @noRd
clean_data <- function(x) {
validate_input(x)
- x_clean <- x[!is.na(x)]
- if (length(x_clean) == 0) {
- stop("No valid data remaining after removing NA values", call. = FALSE)
- }
- x_clean
+ x[!is.na(x)]
}
diff --git a/vignettes/articles/package-structure.qmd b/vignettes/articles/package-structure.qmd
index c9bb6079..16e2251d 100644
--- a/vignettes/articles/package-structure.qmd
+++ b/vignettes/articles/package-structure.qmd
@@ -329,9 +329,9 @@ This article demonstrated how to use `foodwebr` to visualize and understand pack
- Regular visualization helps maintain clean architecture
The `rpt` package demonstrates a clean layered architecture with:
-- Low-level utility functions (`validate_input`, `format_result`)
-- Mid-level processing functions (`clean_data`, `calculate_statistic`)
-- High-level API functions (`example_function`, `calculate_summary`)
+- Low-level utility functions (`validate_input()`, `format_result()`)
+- Mid-level processing functions (`clean_data()`, `calculate_statistic()`)
+- High-level API functions (`example_function()`, `calculate_summary()`)
This structure makes the code easier to understand, test, and maintain.
From b251d7029ff6aebbfee88ac458f15cbb3d9d6861 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Feb 2026 18:22:17 +0000
Subject: [PATCH 6/9] Fix workflow failures: increment version, add NEWS entry,
fix lint and docs
Co-authored-by: d-morrison <2474437+d-morrison@users.noreply.github.com>
---
DESCRIPTION | 3 ++-
NEWS.md | 4 ++++
R/validate_input.R | 6 +++---
inst/WORDLIST | 8 ++++++++
vignettes/articles/package-structure.qmd | 1 +
5 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
index c4f27852..863df5c3 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,6 +1,6 @@
Package: rpt
Title: What the Package Does (One Line, Title Case)
-Version: 0.0.0.9010
+Version: 0.0.0.9011
Authors@R: c(
person("First", "Last", , "first.last@example.com", role = c("aut", "cre"))
)
@@ -15,6 +15,7 @@ Language: en-US
Depends:
R (>= 4.1.0)
Imports:
+ cli,
stats
Suggests:
altdoc,
diff --git a/NEWS.md b/NEWS.md
index db48a7fa..17a14a40 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,9 @@
# rpt (development version)
+* Added package structure visualization article using foodwebr to demonstrate function dependencies and call graphs
+* Created layered helper function architecture for realistic foodwebr demonstrations
+* Added `calculate_summary()` function for computing summary statistics
+
* Switched from pkgdown to altdoc for documentation generation. Now using Quarto Website for documentation with native math equation support via MathJax.
* Removed pkgdown-specific configurations and workflows.
* Retained RevealJS multi-format support for Quarto vignettes and articles.
diff --git a/R/validate_input.R b/R/validate_input.R
index cb4cf91d..d02e2e6f 100644
--- a/R/validate_input.R
+++ b/R/validate_input.R
@@ -9,13 +9,13 @@
#' @noRd
validate_input <- function(x) {
if (!is.numeric(x)) {
- stop("Input must be numeric", call. = FALSE)
+ cli::cli_abort("Input must be numeric", call = NULL)
}
if (length(x) == 0) {
- stop("Input must have at least one element", call. = FALSE)
+ cli::cli_abort("Input must have at least one element", call = NULL)
}
if (all(is.na(x))) {
- stop("Input cannot be all NA values", call. = FALSE)
+ cli::cli_abort("Input cannot be all NA values", call = NULL)
}
TRUE
}
diff --git a/inst/WORDLIST b/inst/WORDLIST
index c870787c..0ec290e1 100644
--- a/inst/WORDLIST
+++ b/inst/WORDLIST
@@ -2,8 +2,10 @@ CMD
Callouts
CodeFactor
Codecov
+DiagrammeR
Lifecycle
MathJax
+README
RevealJS
Tabsets
ackage
@@ -11,12 +13,18 @@ altdoc
callout
callouts
cdot
+codebases
emplate
foldable
+foodweb
+foodwebr
+formatter
frac
+graphviz
linter
lintr
mc
pkgdown
serodynamics
tabset
+tidygraph
diff --git a/vignettes/articles/package-structure.qmd b/vignettes/articles/package-structure.qmd
index 16e2251d..891b0dcf 100644
--- a/vignettes/articles/package-structure.qmd
+++ b/vignettes/articles/package-structure.qmd
@@ -2,6 +2,7 @@
title: "Package Function Structure"
author: "UC Davis Seroepidemiology Research Group (UCD-SERG)"
date: "`r Sys.Date()`"
+prefer-html: true
format:
revealjs:
output-file: package-structure-revealjs.html
From 2e67d68dd04f16e6704dcbf2b700aed1b1773e00 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Feb 2026 18:31:09 +0000
Subject: [PATCH 7/9] Remove DOCX format from vignettes to fix foodwebr HTML
widget rendering
Co-authored-by: d-morrison <2474437+d-morrison@users.noreply.github.com>
---
vignettes/_metadata.yml | 2 --
vignettes/articles/package-structure.qmd | 2 --
2 files changed, 4 deletions(-)
diff --git a/vignettes/_metadata.yml b/vignettes/_metadata.yml
index 78eeb23c..0787245f 100644
--- a/vignettes/_metadata.yml
+++ b/vignettes/_metadata.yml
@@ -16,5 +16,3 @@ format:
number-sections: true
number-depth: 2
fig-cap-location: top
- docx:
- number-sections: true
diff --git a/vignettes/articles/package-structure.qmd b/vignettes/articles/package-structure.qmd
index 891b0dcf..c1a6a592 100644
--- a/vignettes/articles/package-structure.qmd
+++ b/vignettes/articles/package-structure.qmd
@@ -2,12 +2,10 @@
title: "Package Function Structure"
author: "UC Davis Seroepidemiology Research Group (UCD-SERG)"
date: "`r Sys.Date()`"
-prefer-html: true
format:
revealjs:
output-file: package-structure-revealjs.html
html: default
- docx: default
---
```{r}
From 986f7424f20032dec4263efc8edd208eea239e36 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 3 Feb 2026 12:01:23 +0000
Subject: [PATCH 8/9] Restore DOCX format and add package-structure to website
navigation
Co-authored-by: d-morrison <2474437+d-morrison@users.noreply.github.com>
---
altdoc/quarto_website.yml | 3 +++
vignettes/_metadata.yml | 2 ++
vignettes/articles/package-structure.qmd | 10 ++++++----
3 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/altdoc/quarto_website.yml b/altdoc/quarto_website.yml
index 3a4362cf..1ef327ee 100644
--- a/altdoc/quarto_website.yml
+++ b/altdoc/quarto_website.yml
@@ -34,6 +34,8 @@ website:
file: vignettes/quarto_vignette.qmd
- text: "Advanced"
file: vignettes/articles/quarto_article.qmd
+ - text: "Package Structure"
+ file: vignettes/articles/package-structure.qmd
sidebar:
collapse-level: 1
contents:
@@ -48,6 +50,7 @@ website:
- section: Advanced
contents:
- vignettes/articles/quarto_article.qmd
+ - vignettes/articles/package-structure.qmd
- section: $ALTDOC_MAN_BLOCK
style: floating
- text: News
diff --git a/vignettes/_metadata.yml b/vignettes/_metadata.yml
index 0787245f..78eeb23c 100644
--- a/vignettes/_metadata.yml
+++ b/vignettes/_metadata.yml
@@ -16,3 +16,5 @@ format:
number-sections: true
number-depth: 2
fig-cap-location: top
+ docx:
+ number-sections: true
diff --git a/vignettes/articles/package-structure.qmd b/vignettes/articles/package-structure.qmd
index c1a6a592..1e102155 100644
--- a/vignettes/articles/package-structure.qmd
+++ b/vignettes/articles/package-structure.qmd
@@ -16,6 +16,8 @@ knitr::opts_chunk$set(
fig.width = 7,
fig.height = 5
)
+# Check if we're rendering to a format that supports HTML widgets
+is_html_output <- knitr::is_html_output()
```
```{r setup}
@@ -80,7 +82,7 @@ The `plot()` method creates an interactive visualization using DiagrammeR:
```{r}
#| label: fig-basic-foodweb
#| fig-cap: "Basic function dependency graph for example_function"
-#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE) && is_html_output
plot(fw)
```
@@ -123,7 +125,7 @@ fw_pkg
```{r}
#| label: fig-package-foodweb
#| fig-cap: "Complete dependency graph for all package functions"
-#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE) && is_html_output
plot(fw_pkg)
```
@@ -278,7 +280,7 @@ You can focus on specific functions to understand their dependencies:
```{r}
#| label: fig-example-function
#| fig-cap: "Dependency graph for example_function showing all related functions"
-#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE) && is_html_output
fw_example <- foodweb(FUN = example_function)
plot(fw_example)
```
@@ -288,7 +290,7 @@ plot(fw_example)
```{r}
#| label: fig-summary-function
#| fig-cap: "Dependency graph for calculate_summary showing its relationships"
-#| eval: !expr requireNamespace("foodwebr", quietly = TRUE)
+#| eval: !expr requireNamespace("foodwebr", quietly = TRUE) && is_html_output
fw_summary <- foodweb(FUN = calculate_summary)
plot(fw_summary)
```
From 699e4501b0084c31dc7307e7d3e61e6bee90f5d8 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 8 Apr 2026 22:19:18 +0000
Subject: [PATCH 9/9] Add _snaps directory to gitignore to fix macOS build
Agent-Logs-Url: https://github.com/UCD-SERG/rpt/sessions/5481ca55-3b4b-4b97-ac1a-f32a17d5e289
Co-authored-by: d-morrison <2474437+d-morrison@users.noreply.github.com>
---
.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/.gitignore b/.gitignore
index 8eb30ad5..fd43895c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,4 @@ _quarto/*
!_quarto/_freeze/
*.docx
*.knit.md
+tests/testthat/_snaps/