feat: cog_recipes + recipe= + signposting suggestions
Adds cog_recipes() to list the curated harmonization_recipes catalog (24 recipes / schema_version >= 5), and a recipe= argument on cog_spending()/ cog_revenue() that runs a recipe's generic multi-code join instead of the category view: SUM(amt * weight) across whichever component codes are present for a (year, canonical_govid), scoped by gov_type_scope. The join deliberately does not filter is_aggregate -- the wide era (<= 2011) exposes these split families (corrections 04+05, IG *89/*47, U4- rents, etc.) ONLY as aggregate rows, with leaf codes first appearing in 2012, so excluding aggregates would zero out the wide-era half of every recipe. This is safe by corpus construction: wide-era rows are aggregate-only, modern rows are leaf-only, and every component is year-scoped, so there is no double-counting. recipe= is mutually exclusive with category=; the result's subtype column reads "recipe" and category reads the recipe's label. Adds recipe-component-driven signposting: when a basis="harmonized" + category query comes back with zero rows in a requested year, and a harmonization recipe covering that category would actually produce rows for this government in that year (via the same join .run_recipe() uses), the recipe is surfaced in provenance$suggestions plus one cli::cli_inform() message. This is deliberately keyed off recipe components rather than harmonization_map's suggested_recipe_id column (which is empty on every live row -- the wide era's split families are NA-by-construction via aggregate exclusion, not an NA ruling to hang a suggestion off of). Also populates the previously-always-empty provenance$series_break_refs (schema v5 only: series_breaks_pq rows whose fin_code is among the observed codes and whose break_year falls in the requested span), and extends cog_explain() with Basis/Harmonization/Recipe/Suggestions/Series breaks sections.
This commit is contained in:
@@ -51,6 +51,29 @@ test_that("the REPLACE(harmonized_code AS item_code) pattern folds a collapsed c
|
||||
DBI::dbExecute(con, "DROP TABLE synthetic_long")
|
||||
})
|
||||
|
||||
test_that(".build_series_break_refs matches fin_code + break_year window", {
|
||||
# No series_breaks_pq row falls inside the bundled fixture's 2011-2020
|
||||
# window (data-verified; see the "series_break_refs" test in
|
||||
# test-spending.R), so this proves the matching logic itself against the
|
||||
# live view + a synthetic year window that DOES hit a cataloged break
|
||||
# (SB075, fin_code E62, break_year 2005).
|
||||
skip_if_no_corpus()
|
||||
con <- cog_open()
|
||||
on.exit(cog_close())
|
||||
refs <- uscogdata:::.build_series_break_refs(
|
||||
con, codes_observed = c("E62", "E04"), years = c(2003L, 2006L),
|
||||
schema_version = 5L
|
||||
)
|
||||
expect_true("SB075" %in% refs)
|
||||
expect_true("SB071" %in% refs)
|
||||
|
||||
# Gated on schema_version >= 5 even when the codes/years would otherwise match.
|
||||
refs_v4 <- uscogdata:::.build_series_break_refs(
|
||||
con, codes_observed = c("E62"), years = c(2003L, 2006L), schema_version = 4L
|
||||
)
|
||||
expect_equal(refs_v4, character(0))
|
||||
})
|
||||
|
||||
test_that("schema v5 harmonization views register when the corpus supports them", {
|
||||
skip_if_no_corpus()
|
||||
con <- cog_open()
|
||||
|
||||
Reference in New Issue
Block a user