HSH → QuACK — How to Run It

Plain-language manual. The whole thing is: update a few R parameters → run R → refresh Excel → copy values into program tabs. Everything below is detail on those four moves.


How the pieces fit (read once)

  • R computes every metric and writes ONE file per quarter: hsh_feed_Q#_FY##-##.csv. R never opens QuACK.
  • QuACK reads that file through one hidden feed table per quarter (Feed_Q3, Feed_Q4, …). Formulas look values up from it.
  • Three views, all reading the same feed, none of them extra data:
    • All Programs Q# tab — the wide grid. Kept as a QC / validation surface (spot blanks, sanity-check, compare quarters). Not the copy source.
    • By Program tab — a filtered list, one program at a time. This is what you copy values from into each program tab.
    • Raw Counts (optional) — same grid but shows numerator/denominator; for Public Funding if they want raw parts.
  • Program tabs are hand-carried (copy-paste values from By Program). They are not wired to the feed.

Key rule: formulas point at the table name (Feed_Q#), not the sheet. Keep that name exact and everything works.


EVERY QUARTER — the routine

1. R — update parameters, run

2. Excel — load the quarter’s feed (first time that quarter only)

3. Excel — refresh (every run after the first)

4. Wire the All Programs Q# tab (first time that quarter only)

  • =IF($AC3="","",IFERROR(IF(XLOOKUP($AC3&"|"&B$52,Feed_Q4[key],Feed_Q4[na_flag])="Y","NA",XLOOKUP($AC3&"|"&B$52,Feed_Q4[key],Feed_Q4[value])),""))

5. By Program — add this quarter’s block

  • =SORT(CHOOSECOLS(FILTER(Feed_Q4, Feed_Q4[Program Name]=$A$20, "No rows"), 10,6,7,8,11), 5)

    (Columns 10,6,7,8,11 = metric_label, numerator, denominator, value, sort_order; sorted by sort_order.)

6. Carry values into program tabs


READING THE OUTPUT

  • A number = computed value.
  • NA = metric applies but couldn’t be computed (no eligible clients). Distinct from 0%.
  • Blank = metric doesn’t apply to that program. (If a should-apply cell is blank, it’s usually a name mismatch — see Troubleshooting.)
  • Rates come in as decimals (0.69); the program-tab cell formatting shows them as % — the value is correct either way. Just make sure the query’s value column is Decimal, not Whole Number, or rates truncate to 0/1.

NEW FISCAL YEAR — building a fresh QuACK

Most of this ports over unchanged. Per new workbook:

The recurring per-quarter work never changes: 3–4 R parameters, run, refresh, copy.


PENDING ITEMS (confirm before fully trusting those rows)

  • Pathways R40 (Q4): Q3 had one shared Pathways Intake; Q4 will have two (one per Pathways program). Revisit the R40 subsidy calc for Pathways 1 / Pathways 2 when the two intakes are on the roster.
  • Intake names: subsidy_map uses YAC Intake / Rising Up Intake / SOAR Intake (from Section 0). Confirm each quarter if the roster changes.
  • R46/47/48 (street-only) & R49 (supply kits): not computed — hand-enter.
  • R15 Independent Housing: Lark’s exit now reports on R13 (Stable Housing). R15 is empty unless something else uses it.

CHANGING METRICS OR PARAMETERS

You can handle program add/remove yourself (edit the program vector in that metric’s file). These two are trickier because they touch more than one place:

A parameter changes (e.g. a day threshold: 60 → 90)

Contained to R — no crosswalk, no Excel changes.

Note: if the change alters the eligible cohort, an empty cohort may now flip to NA (or back) — that’s intended behavior, not a bug.

A brand-new metric is added

Touches R and the finalize crosswalk and the master tab. Do R first, then wire Excel.

In R: - [ ] Write the metric in the appropriate file (or a new file sourced before finalize). It must append to results_long with the standard columns: metric_id, \Program Name`, numerator, denominator, value. - [ ] Give it a uniquemetric_id. If a program with an empty/zero cohort should showNA(not vanish), emit every listed program with a zero-fillright_join` (copy the pattern from an existing metric).

In hsh_finalize_feed.R: - [ ] Add the new metric_idrow_id line to the crosswalk (map it to the master row it belongs on; program disambiguates, so several metric_ids can share one row_id). - [ ] Add the row_id → label line to row_labels (friendly name; it auto-gets sort_order from the row number). - [ ] Run. Watch the console — a “missing from crosswalk” warning means you emitted a metric_id you didn’t map; a “duplicate keys” warning means two metrics landed on the same row_id + program.

In Excel (that master tab): - [ ] Put the new row_id in the helper column (AC) on the metric’s master row. - [ ] Make sure that row has the fill formula across its program columns (copy from an adjacent wired row). - [ ] Refresh All → confirm it populates. - [ ] The By Program and Raw Counts views need nothing — they read all rows automatically.

If it’s a new master ROW (not just a new column of an existing metric): - [ ] Insert the row on the master tab; extend the helper column and formula onto it. - [ ] Row numbers shifted — confirm the crosswalk row_ids still line up with the master’s actual rows.

A brand-new program is added (never in the data/workbook before)

Different from “add a program to a metric” (below). A brand-new program has to be introduced in the data, the metric logic, and the QuACK columns.

In R: - [ ] Confirm the program appears in active (and its assessment/service files) with a consistent Program Name — check active |> distinct(\Program Name`). - [ ] Add it to the program vector of **every metric it reports on** (may be several files). - [ ] If it's an RRH/subsidy-type program, add it toplacement_lookup/subsidy_map/intake_programs` as needed. - [ ] Run. The feed carries it automatically once metrics emit it — no crosswalk change (crosswalk is per-metric, not per-program).

In Excel (master tab) — a brand-new program is a new COLUMN: - [ ] Add the program’s display header in the column header row. - [ ] Add its row-52 helper entry = the program’s exact feed Program Name. - [ ] Extend the fill formula into that new column (copy an adjacent program column across). - [ ] Refresh All → confirm the column populates. - [ ] By Program and Raw Counts need nothing — they read all programs automatically.

Note: removing a program entirely is the reverse — pull it from the metric vectors in R; the master column can be left or deleted.

Adding an EXISTING program to one more metric

The easy case: the program already lives in the data and the workbook, you’re just including it in one more metric’s list. - [ ] Add it to that metric’s program vector in R. Run → Refresh. Done. (Its master column already exists.)

Rule of thumb: - New metric = 3 places (metric file, crosswalk + labels, master helper/formula). - New program = R metric vectors (maybe several) + a new master COLUMN (header, row-52 name, formula). - Parameter change = 1 place (the metric’s call). - Existing program → one more metric = 1 place (that metric’s vector).


TROUBLESHOOTING (things that actually happened)

  • Refresh error “column ‘value’ wasn’t found”: a feed column moved/was added. Edit query → delete the Changed Type step → Close & Load. (Always add new feed columns at the END, never the middle.)
  • Cells show #REF!: the feed table got deleted while formulas depended on it — Excel baked in #REF!. Re-paste the formula (the table name resolves once it exists again). Don’t delete a query a formula depends on; repoint its Source instead.
  • Whole program column blank though the feed has data: the row-52 name for that column doesn’t match the feed’s Program Name exactly. Fix the row-52 cell. (Watch: DiamondDiamond Youth Shelter, CYHICastro Youth Housing Initiative, Casa Ade.Casa Adelante, Routz (PSH)Routz SH.)
  • Whole metric row blank: the metric’s program-name strings in R don’t match active (e.g. Pathways I RRH vs Pathways 1). Run active |> distinct(\Program Name`)` and match exactly.
  • A metric emits under NA program name: the source file’s program column isn’t Program Name (e.g. ProgramName). Rename at load in Section 0.
  • Denominator column looks empty at the top of the feed: count metrics have no denominator by design; the feed sorts counts first. Scroll to a rate row (R9, R12, R21) — denominators are there.
  • PivotTable / Design ribbon missing in the browser: the browser Excel has a limited pivot ribbon. Use the By Program FILTER view instead (fully browser-safe), or open the file via “Open in Desktop App” — but do NOT File → Download (that forks a local copy). Opening the SharePoint file in desktop edits it in place.
  • Data Validation rejects a formula source: it won’t take a spill/table formula directly. Spill the list into helper cells first (=SORT(UNIQUE(Feed_Q#[Program Name]))), then point the dropdown at that range (=$H$1# or a fixed range).

QUICK REFERENCE

  • Feed columns: period, key, row_id, metric_id, Program Name, numerator, denominator, value, na_flag, metric_label, sort_order
  • Lookup key: row_id | Program Name (e.g. R9|LEASE)
  • Master formula: =IF($AC3="","",IFERROR(IF(XLOOKUP($AC3&"|"&B$52,Feed_Q#[key],Feed_Q#[na_flag])="Y","NA",XLOOKUP($AC3&"|"&B$52,Feed_Q#[key],Feed_Q#[value])),""))
  • By Program formula: =SORT(CHOOSECOLS(FILTER(Feed_Q#, Feed_Q#[Program Name]=$A$1, "No rows"), 10,6,7,8,11), 5)
  • Raw counts formula: =IF($AC3="","",IFERROR(LET(k,$AC3&"|"&B$52,f,XLOOKUP(k,Feed_Q#[key],Feed_Q#[na_flag]),n,XLOOKUP(k,Feed_Q#[key],Feed_Q#[numerator]),d,XLOOKUP(k,Feed_Q#[key],Feed_Q#[denominator]),IF(f="Y","NA",IF(d>0,n&" / "&d,n))),""))
  • Source of truth for names: active |> distinct(\Program Name`)in R; theProgram Name` column in the feed CSV.
  • R run order: Section 0 → all metric sections → finalize LAST.