From 7522b48a08bd0482a71a096886e75527c6af8228 Mon Sep 17 00:00:00 2001 From: Jared Knowles Date: Tue, 4 Aug 2026 22:02:01 -0400 Subject: [PATCH] feat: report suppressed component dollars in the signpost message (#9) .inform_suggestions() and cog_explain() now render suppressed_amount / suppressed_years / suppressed_codes as a continuation line on each suggestion bullet whenever suppressed_amount > 0 (an empty_year fire can carry them too, so this keys off the amount, not trigger). Also renames the cli header from "Coverage gap detected" to "Incomplete coverage" -- a partial-coverage fire is not a gap, the year has rows, they're just short. --- R/explain.R | 9 ++++++++- R/suggestions.R | 17 +++++++++++++++-- tests/testthat/test-recipes.R | 26 ++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/R/explain.R b/R/explain.R index 37380c4..773b9a9 100644 --- a/R/explain.R +++ b/R/explain.R @@ -125,8 +125,15 @@ cog_explain <- function(result, format = c("print", "list")) { if (length(prov$suggestions) > 0L) { cli::cli_h2("Suggestions") sugg_lines <- vapply(prov$suggestions, function(s) { - sprintf("%s -- %s (years %s-%s): %s", s$recipe_id, s$label, + line <- sprintf("%s -- %s (years %s-%s): %s", s$recipe_id, s$label, s$available_years[1], s$available_years[2], s$hint) + if (isTRUE(s$suppressed_amount > 0)) { + line <- paste0(line, sprintf(" [$%s excluded from %s: %s]", + formatC(s$suppressed_amount, format = "f", digits = 0, big.mark = ","), + paste0("FY", s$suppressed_years, collapse = ", "), + paste(s$suppressed_codes, collapse = ", "))) + } + line }, character(1)) cli::cli_ul(sugg_lines) } diff --git a/R/suggestions.R b/R/suggestions.R index 1163f06..4bfa6cf 100644 --- a/R/suggestions.R +++ b/R/suggestions.R @@ -357,12 +357,25 @@ #' expressions. When a suggestion has an `ig_recipe_id`, one indented #' continuation line is appended naming the intergovernmental counterpart #' recipe (embedded `\n` renders as a hanging-indent continuation of the -#' same bullet under cli, not a new bullet). +#' same bullet under cli, not a new bullet). Same treatment for +#' `suppressed_amount` (uscogdata#9): only present when dollars were +#' actually measured as excluded (an `empty_year` fire can carry them too -- +#' see `.build_suggestions()` -- so this keys off the amount, not `trigger`). #' @noRd .inform_suggestions <- function(suggestions) { bullets <- vapply(suggestions, function(s) { bullet <- sprintf("%s (%d-%d): %s", s$recipe_id, s$available_years[1], s$available_years[2], s$hint) + # Only present when dollars were actually measured as excluded. An + # empty_year fire can carry them too -- the year had no rows AND the + # component was suppressed -- which is strictly more informative. + if (isTRUE(s$suppressed_amount > 0)) { + bullet <- paste0(bullet, sprintf( + "\n $%s excluded from %s (%s), published as an aggregate or outside the crosswalk", + formatC(s$suppressed_amount, format = "f", digits = 0, big.mark = ","), + paste0("FY", s$suppressed_years, collapse = ", "), + paste(s$suppressed_codes, collapse = ", "))) + } if (!is.null(s$ig_recipe_id)) { bullet <- paste0(bullet, sprintf( "\n intergovernmental counterpart: recipe = '%s'", s$ig_recipe_id)) @@ -370,7 +383,7 @@ bullet }, character(1)) cli::cli_inform(c( - i = "Coverage gap detected for the requested years; a harmonization recipe may fill it:", + i = "Incomplete coverage for the requested years; a harmonization recipe may fill it:", stats::setNames(bullets, rep("*", length(bullets))) )) } diff --git a/tests/testthat/test-recipes.R b/tests/testthat/test-recipes.R index 612402b..9f6ae57 100644 --- a/tests/testthat/test-recipes.R +++ b/tests/testthat/test-recipes.R @@ -378,3 +378,29 @@ test_that("uscogdata#9: leaf-and-classified wide-era families never fire", { AND v.year = l.year AND v.item_code = l.item_code)")$n expect_equal(as.integer(n), 0L) }) + +test_that("uscogdata#9: the cli message reports the suppressed dollars", { + skip_if_no_corpus() + expect_message( + cog_spending("061037123085", years = 2011L, category = "Public Welfare"), + "1,803,872,000", fixed = TRUE) + expect_message( + cog_spending("061037123085", years = 2011L, category = "Public Welfare"), + "FY2011", fixed = TRUE) + expect_message( + cog_spending("061037123085", years = 2011L, category = "Public Welfare"), + "E67", fixed = TRUE) +}) + +test_that("uscogdata#9: cog_explain() reports the suppressed dollars", { + # cog_explain()'s whole "print" output -- including the Suggestions + # section built from cli::cli_ul() -- is emitted on the message stream + # (verified empirically 2026-08-04: capture.output(..., type = "output") + # returns character(0) for this call; testthat::capture_messages() is what + # actually carries it), so that is the stream this test captures. + skip_if_no_corpus() + r <- suppressMessages( + cog_spending("061037123085", years = 2011L, category = "Public Welfare")) + out <- paste(testthat::capture_messages(cog_explain(r)), collapse = "") + expect_match(out, "271,589,000", fixed = TRUE) +})