open-navigator / examples /demo_fec_integration.py
jcbowyer's picture
Clean HuggingFace deployment without binary files
61d29fc
#!/usr/bin/env python3
"""
FEC Campaign Finance Integration Demo
Demonstrates how to use the FEC (Federal Election Commission) API integration
to track political contributions and campaign finance data.
Use Cases:
1. Track political donations from nonprofit leadership
2. Identify politically active individuals in advocacy organizations
3. Analyze campaign finance patterns in policy-related sectors
4. Map donor networks in healthcare and social services
Prerequisites:
1. Get a free FEC API key: https://api.data.gov/signup/
2. Set environment variable: export FEC_API_KEY="your_key_here"
3. Or use DEMO_KEY (limited to 30 requests/hour)
Usage:
# Using DEMO_KEY (limited):
python examples/demo_fec_integration.py --state MA
# With your own API key:
export FEC_API_KEY="your_key_here"
python examples/demo_fec_integration.py --state MA --cycle 2024
# Search for specific nonprofit donors:
python examples/demo_fec_integration.py --state MA --employer "Community Health"
# Create full gold tables:
python examples/demo_fec_integration.py --state MA --create-gold-tables
API Documentation: https://api.open.fec.gov/developers/
"""
import sys
from pathlib import Path
import argparse
import os
# Add project root to path
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
from discovery.fec_integration import OpenFECAPI
from pipeline.create_campaigns_gold_tables import CampaignsGoldTableCreator
from loguru import logger
def demo_search_candidates(api: OpenFECAPI, state: str, cycle: int = 2024):
"""Demo: Search for candidates in a state"""
logger.info("=" * 60)
logger.info(f"DEMO: Searching for {state} candidates in {cycle}")
logger.info("=" * 60)
for office, office_name in [('H', 'House'), ('S', 'Senate')]:
logger.info(f"\n{office_name} Candidates:")
result = api.search_candidates(
state=state,
office=office,
cycle=cycle,
per_page=10
)
candidates = result.get('results', [])
for candidate in candidates:
logger.info(f" • {candidate.get('name')} ({candidate.get('party')})")
logger.info(f" District: {candidate.get('district') or 'N/A'}")
logger.info(f" ID: {candidate.get('candidate_id')}")
def demo_search_contributions(
api: OpenFECAPI,
state: str,
employer: str = None,
min_amount: float = 1000.0
):
"""Demo: Search for individual contributions"""
logger.info("=" * 60)
logger.info(f"DEMO: Searching for contributions from {state}")
if employer:
logger.info(f" Employer filter: {employer}")
logger.info(f" Min amount: ${min_amount}")
logger.info("=" * 60)
result = api.search_individual_contributions(
contributor_state=state,
contributor_employer=employer,
min_amount=min_amount,
per_page=10
)
contributions = result.get('results', [])
pagination = result.get('pagination', {})
logger.info(f"\nFound {pagination.get('count', 0):,} total contributions")
logger.info(f"Showing first {len(contributions)} records:\n")
for contrib in contributions:
logger.info(f" • {contrib.get('contributor_name')}")
logger.info(f" Employer: {contrib.get('contributor_employer')}")
logger.info(f" Occupation: {contrib.get('contributor_occupation')}")
logger.info(f" Amount: ${contrib.get('contribution_receipt_amount'):,.2f}")
logger.info(f" Date: {contrib.get('contribution_receipt_date')}")
logger.info(f" Recipient: {contrib.get('committee_name')}")
logger.info("")
def demo_search_committees(api: OpenFECAPI, state: str):
"""Demo: Search for PACs and committees"""
logger.info("=" * 60)
logger.info(f"DEMO: Searching for committees in {state}")
logger.info("=" * 60)
result = api.search_committees(
state=state,
per_page=10
)
committees = result.get('results', [])
logger.info(f"\nFound {len(committees)} committees:\n")
for committee in committees:
logger.info(f" • {committee.get('name')}")
logger.info(f" Type: {committee.get('committee_type_full')}")
logger.info(f" Party: {committee.get('party') or 'N/A'}")
logger.info(f" ID: {committee.get('committee_id')}")
logger.info("")
def demo_nonprofit_donor_tracking(api: OpenFECAPI, state: str):
"""
Demo: Track political contributions from nonprofit sector
This is the KEY use case for civic engagement platform:
- Identify nonprofit leaders who are politically active
- Track donor networks in advocacy organizations
- Analyze political influence on grant decisions
"""
logger.info("=" * 60)
logger.info(f"DEMO: Tracking nonprofit leadership donations in {state}")
logger.info("=" * 60)
# Common nonprofit employer keywords
nonprofit_keywords = [
"Foundation",
"Community Health",
"Association",
"Council",
"Alliance",
"Institute",
]
logger.info("\nSearching for contributions from nonprofit employees...\n")
all_contributions = []
for keyword in nonprofit_keywords[:3]: # Limit for demo
logger.info(f"Searching employer: {keyword}")
try:
result = api.search_individual_contributions(
contributor_state=state,
contributor_employer=keyword,
min_amount=200.0, # FEC reporting threshold
per_page=5
)
contributions = result.get('results', [])
all_contributions.extend(contributions)
logger.info(f" Found {len(contributions)} contributions")
except Exception as e:
logger.warning(f" Error: {e}")
if all_contributions:
logger.info(f"\n📊 SUMMARY:")
logger.info(f"Total contributions found: {len(all_contributions)}")
# Calculate totals
total_amount = sum(
contrib.get('contribution_receipt_amount', 0)
for contrib in all_contributions
)
logger.info(f"Total amount: ${total_amount:,.2f}")
logger.info(f"\nTop donors:")
for contrib in all_contributions[:5]:
logger.info(f" • {contrib.get('contributor_name')}")
logger.info(f" {contrib.get('contributor_employer')}")
logger.info(f" ${contrib.get('contribution_receipt_amount'):,.2f}{contrib.get('committee_name')}")
logger.info("")
def demo_create_gold_tables(state: str, cycle: int = 2024):
"""Demo: Create full campaign finance gold tables"""
logger.info("=" * 60)
logger.info(f"DEMO: Creating campaign finance gold tables for {state}")
logger.info("=" * 60)
creator = CampaignsGoldTableCreator(
state_code=state,
api_key=os.getenv('FEC_API_KEY')
)
creator.create_all_campaigns_tables(
cycle=cycle,
min_contribution_amount=200.0,
max_contributions=1000 # Limit for demo
)
def main():
"""Main demo function"""
parser = argparse.ArgumentParser(
description="FEC Campaign Finance Integration Demo"
)
parser.add_argument(
"--state",
default="MA",
help="Two-letter state code (default: MA)"
)
parser.add_argument(
"--cycle",
type=int,
default=2024,
help="Election cycle year (default: 2024)"
)
parser.add_argument(
"--employer",
help="Search for contributions from specific employer"
)
parser.add_argument(
"--create-gold-tables",
action="store_true",
help="Create full campaign finance gold tables"
)
args = parser.parse_args()
# Validate state
state = args.state.upper()
if len(state) != 2:
logger.error("State must be 2-letter code (e.g., MA, AL, GA)")
return
# Initialize FEC API
api_key = os.getenv('FEC_API_KEY', 'DEMO_KEY')
if api_key == 'DEMO_KEY':
logger.warning("Using DEMO_KEY (limited to 30 requests/hour)")
logger.warning("Get your free API key: https://api.data.gov/signup/")
logger.warning("Then: export FEC_API_KEY='your_key_here'\n")
api = OpenFECAPI(api_key=api_key)
# Run demos
if args.create_gold_tables:
demo_create_gold_tables(state, args.cycle)
else:
# Run quick API demos
demo_search_candidates(api, state, args.cycle)
print("\n" + "=" * 60 + "\n")
demo_search_committees(api, state)
print("\n" + "=" * 60 + "\n")
if args.employer:
demo_search_contributions(api, state, employer=args.employer)
else:
demo_nonprofit_donor_tracking(api, state)
logger.success("\n✅ Demo complete!")
logger.info("\nNext steps:")
logger.info(" 1. Get FEC API key: https://api.data.gov/signup/")
logger.info(" 2. Create gold tables: python examples/demo_fec_integration.py --state MA --create-gold-tables")
logger.info(" 3. Run pipeline: python pipeline/create_campaigns_gold_tables.py --state MA")
if __name__ == "__main__":
main()