R-CMD-check / check (push) Successful in 3m5s
Rewrites expenditure/revenue classification off item-code first-letter
prefixes and onto summary_categories membership (F-018: prefix Y spans
revenue, expenditure, and balance codes), and exposes
expenditure_concept = c("primary", "direct", "total") with primary as
the new default:
primary = operations + capital + assistance
direct = primary + interest + insurance_benefits (Census Direct)
total = direct + intergovernmental (M/L/Q via ig views)
- inst/sql: flow views (20-25) select by crosswalk membership;
summary_categories moves to 11- so it registers before them (DuckDB
binds view sources eagerly). The IG leg gains Q11/Q12/Q18 state
school-system payments (F-017).
- R: one subtype scope per verb call drives the verb SQL, the
harmonization exclusion count, and the complete = TRUE grid;
flow_prefixes survives only to scope recipe suggestions.
cog_geographic_rollup/cog_peer_compare accept primary|direct, still
refuse total, and now actually pass the concept through.
- Balance codes can never reach a spending or revenue result
(uscogdata#25), asserted at both view and verb level.
- Deletes the #11 skip; per the 2026-07-30 owner ruling the F-018 Y01
proof is asserted against the crosswalk, not the default
cog_revenue() call (which stays General Revenue pending #12).
Suite: 696 pass / 0 fail / 1 skip (#12, expected).
Closes #11
88 lines
3.4 KiB
R
88 lines
3.4 KiB
R
# R/basis.R
|
|
# basis= resolution (harmonized/raw, with v4/v5 dual-accept) and the
|
|
# harmonization exclusion-count block attached to provenance.
|
|
|
|
#' Resolve the requested `basis` against the active corpus's schema_version.
|
|
#'
|
|
#' On a `schema_version >= 5` corpus, the requested basis is used as-is. On
|
|
#' an older (`schema_version == 4`) corpus, which has no harmonization
|
|
#' tables: a caller who left `basis` at its default (`"harmonized"`, so
|
|
#' `explicit` is `FALSE`) silently gets `"raw"` back, with a note recorded
|
|
#' for provenance; a caller who explicitly asked for
|
|
#' `basis = "harmonized"` gets a hard abort instead of a silent downgrade.
|
|
#'
|
|
#' @param basis `"harmonized"` or `"raw"` (already resolved via `match.arg`).
|
|
#' @param explicit `TRUE` if the caller passed `basis` explicitly (as
|
|
#' opposed to relying on the default `c("harmonized", "raw")`).
|
|
#' @param manifest The active session's parsed manifest list.
|
|
#' @return List with `basis` (the resolved value) and `note` (character or
|
|
#' `NA_character_`).
|
|
#' @noRd
|
|
.resolve_basis <- function(basis, explicit, manifest) {
|
|
schema_version <- suppressWarnings(as.integer(manifest$schema_version %||% 0L))
|
|
|
|
if (schema_version >= 5L) {
|
|
return(list(basis = basis, note = NA_character_))
|
|
}
|
|
|
|
if (identical(basis, "harmonized") && explicit) {
|
|
cli::cli_abort(c(
|
|
"basis = \"harmonized\" requires corpus schema_version >= 5.",
|
|
x = "Active corpus has schema_version {schema_version}.",
|
|
i = "Use basis = \"raw\" (the default on this corpus), or point USCOGDATA_URL at a schema_version >= 5 corpus."
|
|
), class = "uscogdata_basis_unsupported")
|
|
}
|
|
|
|
list(
|
|
basis = "raw",
|
|
note = sprintf(
|
|
"basis resolved to \"raw\": corpus schema_version %d < 5 (harmonization tables unavailable)",
|
|
schema_version
|
|
)
|
|
)
|
|
}
|
|
|
|
#' Count + sum item-level rows that basis="harmonized" excludes because they
|
|
#' carry no harmonized_code (discontinued / not-yet-ruled codes) within the
|
|
#' calling verb's crosswalk scope (`subtype_col` values in `subtype_scope` --
|
|
#' the same subtype-membership classification the verb SQL uses, never
|
|
#' item-code prefixes), govids, and years. Only meaningful when the resolved
|
|
#' basis is "harmonized"; returns an applied = FALSE stub otherwise (raw
|
|
#' basis never excludes rows this way).
|
|
#'
|
|
#' The intergovernmental leg is deliberately outside this count even for
|
|
#' expenditure_concept = "total": ig_long_harmonized COALESCEs rather than
|
|
#' drops NULL-harmonized rows, so harmonization never excludes an IG row.
|
|
#' @noRd
|
|
.build_harmonization_block <- function(con, govid, years, resolved,
|
|
subtype_col, subtype_scope) {
|
|
if (!identical(resolved$basis, "harmonized")) {
|
|
return(list(
|
|
applied = FALSE,
|
|
na_rows_excluded = 0L,
|
|
na_amount_excluded = 0,
|
|
note = resolved$note
|
|
))
|
|
}
|
|
|
|
sql <- sprintf(
|
|
"SELECT COUNT(*) AS n, COALESCE(SUM(amt), 0) * 1000.0 AS amt
|
|
FROM long
|
|
WHERE canonical_govid IN (%s) AND year IN (%s)
|
|
AND NOT is_aggregate AND harmonized_code IS NULL
|
|
AND item_code IN (
|
|
SELECT item_code FROM summary_categories WHERE %s IN (%s)
|
|
)",
|
|
.sql_lit_chr(govid), paste(as.integer(years), collapse = ","),
|
|
subtype_col, .sql_lit_chr(subtype_scope)
|
|
)
|
|
na <- DBI::dbGetQuery(con, sql)
|
|
|
|
list(
|
|
applied = TRUE,
|
|
na_rows_excluded = as.integer(na$n),
|
|
na_amount_excluded = as.numeric(na$amt),
|
|
note = resolved$note
|
|
)
|
|
}
|