feat(scoring): federal funder precedent via USASpending (ALN-level)
Precedent was always a valid criterion for federal grants — the index just didn't cover them. Now: ingest captures ALN/CFDA numbers from fetchOpportunity (grants.alns); nightly federalPrecedent workflow (04:45) queries USASpending award search per distinct program (free official API, 3-year NH lookback) and stamps program_state_award_count + sample recipients onto open grants (multi-ALN keeps highest). Match retrieval feeds the same funderStateGrantCount input and 25-point tiers foundations use; detail page shows the recipients-evidence table with an incumbent-renewal caution. Also: detail refresh now rotates oldest-verified-first (serverMapGrantVerification) — the Set-based partition re-fetched the same 200 every pass, leaving 365/565 grants ALN-less. Live: 564/564 grants ALN-tagged, 156 programs swept, 85 with NH history, 468 grants carrying precedent, first federal easy-wins (66pts, 25/25 precedent, Aug-24 deadline). 158 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,7 @@ export async function serverGetMatchDetail(
|
||||
grantEligibility: schema.grants.eligibilityEntityTypes,
|
||||
grantGeo: schema.grants.geographicScope,
|
||||
grantEffort: schema.grants.applicationEffortEstimate,
|
||||
grantProgramAwards: schema.grants.programStateAwards,
|
||||
})
|
||||
.from(schema.matches)
|
||||
.innerJoin(schema.orgs, eq(schema.matches.orgId, schema.orgs.id))
|
||||
@@ -109,6 +110,23 @@ export async function serverGetMatchDetail(
|
||||
|
||||
let funderGivingHistory: MatchDetail['funderGivingHistory'] = [];
|
||||
let funderApplicationInfo: unknown = null;
|
||||
if (row.grantFunderEin == null && Array.isArray(row.grantProgramAwards)) {
|
||||
// Federal grants: USASpending program awards fill the same evidence
|
||||
// table (recipient/amount/year) the 990-PF history uses.
|
||||
funderGivingHistory = (
|
||||
row.grantProgramAwards as Array<{
|
||||
recipientName?: string;
|
||||
amount?: number | null;
|
||||
startDate?: string | null;
|
||||
}>
|
||||
).map((a) => ({
|
||||
recipientName: a.recipientName ?? 'Unknown recipient',
|
||||
recipientCity: null,
|
||||
amount: a.amount ?? null,
|
||||
purpose: null,
|
||||
taxYear: a.startDate != null ? Number(a.startDate.slice(0, 4)) : 0,
|
||||
}));
|
||||
}
|
||||
if (row.grantFunderEin != null) {
|
||||
const funderRow = await db
|
||||
.select({ applicationInfo: schema.funders.applicationInfo })
|
||||
|
||||
Reference in New Issue
Block a user