Metadata-Version: 2.4
Name: ocpf
Version: 0.1.0
Summary: An opinionated command-line interface to the OCPF (Massachusetts campaign finance) API
Project-URL: Homepage, https://github.com/bwbensonjr/ocpf-cli
Project-URL: Repository, https://github.com/bwbensonjr/ocpf-cli
Project-URL: Issues, https://github.com/bwbensonjr/ocpf-cli/issues
Author-email: Brent Benson <bwbensonjr@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: campaign-finance,cli,elections,massachusetts,ocpf,politics
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: typer>=0.12
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Description-Content-Type: text/markdown

# OCPF Command Line Interface

`ocpf` is an opinionated command-line interface to the Massachusetts
[Office of Campaign and Political Finance](https://www.ocpf.us/) (OCPF) API
(`https://api.ocpf.us/`). It turns a recurring, multi-step lookup — "who is
running in this district and how much have they raised and spent?" — into a
single command.

## Install

Run it with no install at all using [`uvx`](https://docs.astral.sh/uv/):

```bash
uvx ocpf race 37th
```

Or install the `ocpf` command onto your PATH:

```bash
pipx install ocpf     # isolated, recommended
pip install ocpf      # into the current environment
```

Then:

```bash
ocpf --help
```

## Usage

```bash
ocpf race <district> [--year <year>] [--json]
```

`ocpf race` produces a year-to-date (YTD) financial summary of the legislative
(House or Senate) candidates in a district.

- `<district>` may be a **name** matched case-insensitively against OCPF's
  district descriptions (`&` and `and` are treated alike) or a **raw numeric
  district code**. Ambiguous names are never guessed — the tool prints the
  matching districts with their codes and exits so you can pick one.
- `--year` defaults to the current calendar year.
- `--json` emits the merged, filtered candidate records (including the
  underlying `*Numeric` values) as JSON to stdout. Human status/progress goes
  to stderr, so JSON output stays pipeable.

### Example

```bash
$ ocpf race "Suffolk and Middlesex" --year 2026
District:  Senate, Suffolk and Middlesex (code 166)
Election:  primary 9/1/2026, general 11/3/2026
As of:     6/30/2026 (year-to-date, cumulative)

Candidate                 Party  Inc   Raised YTD    Spent YTD  Cash on Hand
------------------------  -----  ---  -----------  -----------  ------------
Brownsberger, William N.  -      *    $265,435.76  $135,490.74   $326,673.49
Lander, Daniel            -           $117,740.53   $32,674.59   $136,386.28
Wood, Brandon             -                $80.00        $3.00        $77.00

* incumbent (holds this seat)
```

Election dates and the as-of date are **timeline context**. The money is the
single cumulative YTD figure the API provides for each candidate; it is never
split into per-primary and per-general amounts.

### `ocpf filer` — one candidate's filing summary

```bash
ocpf filer <filer> [--year <year>] [--json]
```

`ocpf filer` drills into a single filer: their committee profile, cumulative
YTD finances, and most recent reports.

- `<filer>` may be a **raw numeric cpfId** (works for any filer type) or a
  **candidate name** matched case-insensitively against the legislative field
  for the year. cpfIds are shown by `ocpf race`. As with district names,
  ambiguous names are never guessed — the tool prints the matching filers with
  their cpfIds and exits. Name lookup covers legislative filers; for other
  filer types, pass a cpfId directly.
- `--year` defaults to the current calendar year (used for name resolution and
  YTD context).
- `--json` emits the `filer`, `ytdReport`, and `logReports` records (including
  numeric values) to stdout.

```bash
$ ocpf filer "Brownsberger" --year 2026
Filer:      Brownsberger, William N.  (cpfId 14454)
Committee:  Brownsberger Committee
Party:      Democratic    Type: Legislative Candidates
Office:     Senate, Suffolk and Middlesex
Status:     active
Organized:  12/13/2005
Treasurer:  David Merfeld

Year-to-date (as of 6/30/2026):
  Raised YTD:    $265,435.76
  Spent YTD:     $135,490.74
  Cash on Hand:  $326,673.49

Recent reports:
Type            Period   Filed            Receipts  Expenditures
--------------  -------  --------------  ---------  ------------
Deposit Report  7/20/26  Mon, 7/20/2026    $500.00         $0.00
...
```

## Scope

v1 covers **legislative** races (House and Senate). Other office types
(statewide, county, mayoral, ballot question), drill-down into individual
reports/donors/expenditures, and free-text candidate-name search are out of
scope. See `openspec/` for the design and specifications.

## Development

The project is managed with [`uv`](https://docs.astral.sh/uv/). From a clone of
this repository:

```bash
uv sync --extra dev     # runtime deps + pytest/respx
uv run pytest           # run the test suite
uv run ocpf race 37th   # run the CLI from source
```

## Releasing

Releases are published to PyPI automatically by GitHub Actions when a version tag
is pushed. Versioning is tag-driven (via `hatch-vcs`), so the tag is the single
source of truth for the package version:

```bash
git tag v0.1.0
git push --tags
```

The release workflow runs the test suite, builds the sdist and wheel, and
publishes to PyPI using [Trusted Publishing](https://docs.pypi.org/trusted-publishers/)
(OIDC) — no API token is stored in the repository.

**First release only:** before pushing the first tag, register a *pending*
Trusted Publisher on PyPI (Your projects → Publishing) so the initial upload can
create the project:

- PyPI project name: `ocpf`
- Owner: `bwbensonjr`  ·  Repository: `ocpf-cli`
- Workflow: `release.yml`  ·  Environment: `pypi`

## Design Guidelines

- Use OpenSpec to draft designs and create change proposals.
- Use [clig.dev](https://clig.dev/) for command line interface guidelines.
- Use the `github.com/bwbensonjr/ocpf-analysis` repository (available
  locally) for information about the OCPF APIs, especially the `/api`
  directory which contains `ENDPOINT-STATUS.md` and other script and
  OCPF API test code.
- Use the `github.com/bwbensonjr/ma-election-db` repository (available
  locally) for information on candidates and districts we might want
  to look up.
