feat(search): basket mode for cog_gov_search() #1

Merged
jared merged 12 commits from feat/cog-gov-search-basket-mode into main 2026-04-28 15:09:44 -04:00
2 changed files with 63 additions and 2 deletions
Showing only changes of commit d9bf0552b7 - Show all commits
+28 -2
View File
@@ -282,8 +282,6 @@ cog_gov_search <- function(name = NULL, state = NULL, type = NULL) {
# Orchestrates basket-mode resolution: validate, per-row resolve, # Orchestrates basket-mode resolution: validate, per-row resolve,
# assemble the basket tibble + sidecar, attach the sidecar as an attr. # assemble the basket tibble + sidecar, attach the sidecar as an attr.
# Caller is responsible for emitting any post-resolution summary message
# (see Task 7 — this stays silent for now).
#' @noRd #' @noRd
.resolve_basket <- function(name, state, type, con) { .resolve_basket <- function(name, state, type, con) {
args <- .validate_basket_args(name = name, state = state, type = type) args <- .validate_basket_args(name = name, state = state, type = type)
@@ -305,6 +303,7 @@ cog_gov_search <- function(name = NULL, state = NULL, type = NULL) {
sidecar <- .build_sidecar(args, resolved) sidecar <- .build_sidecar(args, resolved)
attr(basket, "resolution") <- sidecar attr(basket, "resolution") <- sidecar
.basket_summary_message(sidecar)
basket basket
} }
@@ -348,3 +347,30 @@ cog_gov_search <- function(name = NULL, state = NULL, type = NULL) {
int_type <- .coerce_type(type) int_type <- .coerce_type(type)
c("0" = "state", "1" = "county", "2" = "city", "3" = "township")[[as.character(int_type)]] c("0" = "state", "1" = "county", "2" = "city", "3" = "township")[[as.character(int_type)]]
} }
# Single post-resolution summary message. Silent on clean baskets;
# emits one cli_inform with two-line body otherwise.
#' @noRd
.basket_summary_message <- function(sidecar) {
status <- sidecar$status
n_input <- length(status)
n_basket <- sum(status %in% c("resolved", "largest_pop"))
n_amb <- sum(status == "ambiguous")
n_nm <- sum(status == "no_match")
n_lp <- sum(status == "largest_pop")
if (n_amb == 0L && n_nm == 0L && n_lp == 0L) return(invisible(NULL))
parts <- c(
if (n_amb > 0L) sprintf("%d ambiguous", n_amb),
if (n_nm > 0L) sprintf("%d with no match", n_nm),
if (n_lp > 0L) sprintf("%d used largest-population fallback", n_lp)
)
cli::cli_inform(c(
i = sprintf("Basket resolved %d of %d entries.", n_basket, n_input),
i = paste(parts, collapse = ", "),
i = "Inspect with `cog_basket_resolution(result)` or filter to problem rows with `cog_basket_unresolved(result)`."
))
invisible(NULL)
}
+35
View File
@@ -340,3 +340,38 @@ test_that("cog_gov_search basket mode validates argument lengths", {
regexp = "must be length 1 or 3" regexp = "must be length 1 or 3"
) )
}) })
# ---- basket mode summary message ----
test_that("cog_gov_search basket mode is silent on clean basket", {
skip_if_no_corpus()
expect_message(
cog_gov_search(
name = c("BROWARD COUNTY", "SAN DIEGO CITY"),
state = c("FL", "CA")
),
regexp = NA # NA = expect no message
)
})
test_that("cog_gov_search basket mode reports breakdown on partial basket", {
skip_if_no_corpus()
expect_message(
cog_gov_search(
name = c("Broward", "San Diego", "Notarealplace"),
state = c("FL", "CA", "NY")
),
regexp = "Basket resolved 1 of 3"
)
})
test_that("cog_gov_search basket mode message points to the sidecar accessor", {
skip_if_no_corpus()
expect_message(
cog_gov_search(
name = c("Broward", "Notarealplace"),
state = c("FL", "NY")
),
regexp = "cog_basket_resolution"
)
})