Skip to content

feat: add a Blackbaud CRM (Enterprise) gift export reader (ingest preset) #240

Description

@shivamlalakiya

What's needed

Same pattern as the other CRM bridges in philanthropy.ingest (CiviCRM, Raiser's Edge, NPSP): a thin, CRM-specific header-alias map plus a commitment/payment exclusion filter, delegating the roll-up to the shared aggregator in philanthropy/ingest/_civicrm.py. This issue adds the bridge for Blackbaud CRM (the enterprise product built on the BBEC/SKY platform), which is a different product with a different data model from Blackbaud's Raiser's Edge / RE NXT already supported here — do not assume the two share field names or vocabulary. Blackbaud CRM models a gift as a "Revenue" record against a "Constituent", not a Raiser's Edge-style Gift record; confirm the actual shape from the docs before writing any alias.

Where to look

File Purpose
philanthropy/ingest/_raisers_edge.py Closest existing example of the pattern (same vendor, different product — useful for structure, not for field names)
philanthropy/ingest/_civicrm.py The shared aggregator (civicrm_contributions_to_features, read_civicrm_contributions) and its private helpers
tests/test_raisers_edge.py The test depth expected of your new tests/test_blackbaud_crm.py
philanthropy/cli.py _FEATURE_SOURCES and the features --source dispatch — wire your new source in the same shape, unless the read_gifts preset registry (a separate, in-flight PR) has already landed, in which case register there instead

What to build

  1. philanthropy/ingest/_blackbaud_crm.py: a header-alias map from Blackbaud CRM's real export labels (Revenue query/export) and SKY API field names onto the canonical contact_id / receive_date / total_amount / gift_type names; a documented default exclusion parameter for whatever this product's pledge-vs-payment split actually is; a blackbaud_crm_revenue_to_features(...) function (name it for what the product actually calls the record once you've checked); and a read_blackbaud_crm_revenue(path) reader delegating to read_civicrm_contributions.
  2. Verify every field name and every commitment/payment vocabulary term against Blackbaud's own public SKY API / Blackbaud CRM documentation before writing it down. Cite the doc URLs you actually used in your PR body, the way PR #233 does for NPSP. This issue does not assert any field name as fact — confirm everything yourself, and do not carry over any Raiser's Edge field name without checking it's actually the same in this product.
  3. Export the new names from philanthropy/ingest/__init__.py (__all__, alphabetical) and add a stability-tier row to docs/reference/index.md (Tier 2/Beta — a test enforces every public symbol has a row).
  4. tests/test_blackbaud_crm.py at the depth of tests/test_raisers_edge.py: header aliasing from every spelling the docs name, the commitment/payment double-count trap and its exclusion, the exclusion set being documented/overridable/disabled by None, currency parsing, schema/dtypes, empty input, missing required columns.
  5. CHANGELOG.md entry under ## [Unreleased], and add yourself to CONTRIBUTORS.md if you'd like.

Hard constraints (same as the rest of this ingest series)

  • Dependencies stay scikit-learn/pandas/numpy/joblib only — no new package, no API client, no network code anywhere in philanthropy/. File-export reader only, never a live API connector.
  • No new estimator class.
  • Build the exclusion logic from Blackbaud's own public docs for this specific product only — never from an institution's actual configuration or data, and never assumed from Raiser's Edge.
  • Follow AGENTS.md's order: implement, export, verify with python -c "...", then write tests, then make ci.

Verifying

python -m pip install -e ".[dev]"
python -c "from philanthropy.ingest import blackbaud_crm_revenue_to_features, read_blackbaud_crm_revenue; print('OK')"
make ci
make riskcov

Claim this issue with a comment before starting; a maintainer assigns within 24 hours.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions