Skip to content

feat: add a DonorPerfect gift export reader (ingest preset) #236

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 DonorPerfect.

Where to look

File Purpose
philanthropy/ingest/_raisers_edge.py Closest existing example: header aliasing, a documented default exclusion set, delegation to the shared aggregator
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_donorperfect.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/_donorperfect.py: a header-alias map from DonorPerfect's real export labels (its "gifts" table export) and its API field names onto the canonical contact_id / receive_date / total_amount / gift_type names; a documented default exclusion parameter for whatever DonorPerfect's pledge-vs-payment split actually is (DonorPerfect's data model separates pledges from gifts differently than Raiser's Edge or NPSP — find out exactly how from the docs); a donorperfect_gifts_to_features(...) function; and a read_donorperfect_gifts(path) reader delegating to read_civicrm_contributions.
  2. Verify every field name and every commitment/payment vocabulary term against DonorPerfect's own public 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.
  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_donorperfect.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 DonorPerfect's own public docs only — never from an institution's actual configuration or data.
  • 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 donorperfect_gifts_to_features, read_donorperfect_gifts; 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