Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| """ | |
| Voter Registration Data Integration | |
| Track voter registration and political demographics: | |
| - Party affiliation by jurisdiction | |
| - Voter turnout patterns | |
| - Demographic trends | |
| - Elected official party affiliations | |
| Data Sources: | |
| 1. State Voter Files: Most comprehensive (state-by-state) | |
| 2. L2 Political: Commercial voter data aggregator | |
| 3. Aristotle: Commercial political data | |
| 4. VoteRef.com: Public voter lookup (manual) | |
| 5. VSEC (Voter Study Election Consortium): Research data | |
| Note: | |
| Most comprehensive voter data requires: | |
| - State-by-state public records requests | |
| - Commercial vendor licenses (L2, Aristotle) | |
| - Academic research access (VSEC) | |
| For free/open access, we use: | |
| - MIT Election Data Lab | |
| - OpenElections project | |
| - Census voting statistics | |
| """ | |
| import requests | |
| import pandas as pd | |
| from typing import Dict, List, Optional | |
| from pathlib import Path | |
| from loguru import logger | |
| import time | |
| class VoterDataIntegration: | |
| """ | |
| Aggregate voter and election data from public sources | |
| Note: Comprehensive voter files require commercial licenses. | |
| This class focuses on publicly available aggregated data. | |
| """ | |
| def __init__(self): | |
| self.session = requests.Session() | |
| self.session.headers.update({ | |
| 'User-Agent': 'CommunityOne/1.0 (Civic Engagement Platform)' | |
| }) | |
| def get_state_party_registration(self, state_code: str) -> Optional[Dict]: | |
| """ | |
| Get party registration statistics for a state | |
| Note: This requires state-specific APIs or manual data collection. | |
| Each state has different formats and availability. | |
| Args: | |
| state_code: Two-letter state code | |
| Returns: | |
| Party registration statistics (if available) | |
| """ | |
| # Massachusetts example: https://www.sec.state.ma.us/ele/eleenr/enridx.htm | |
| # This would need state-specific implementations | |
| logger.info(f"Party registration data requires state-specific implementation for {state_code}") | |
| logger.info("Consider using commercial vendors (L2, Aristotle) for comprehensive data") | |
| return None | |
| def get_census_voting_stats( | |
| self, | |
| state_code: str, | |
| county_name: Optional[str] = None | |
| ) -> pd.DataFrame: | |
| """ | |
| Get voting statistics from Census Bureau | |
| Census provides: | |
| - Voting-age population | |
| - Registration rates | |
| - Turnout rates | |
| - Demographics of voters | |
| API: https://www.census.gov/data/developers/data-sets/acs-5year.html | |
| Args: | |
| state_code: Two-letter state code | |
| county_name: Optional county name | |
| Returns: | |
| DataFrame with voting statistics | |
| """ | |
| logger.info(f"Census voting statistics for {state_code}") | |
| logger.info("This requires Census API key and specific table queries") | |
| # Placeholder - would implement Census API calls | |
| return pd.DataFrame() | |
| def enrich_jurisdictions_with_party_data( | |
| self, | |
| jurisdictions_df: pd.DataFrame | |
| ) -> pd.DataFrame: | |
| """ | |
| Enrich jurisdiction data with party affiliation estimates | |
| Args: | |
| jurisdictions_df: DataFrame with jurisdictions | |
| Returns: | |
| Enriched DataFrame | |
| """ | |
| logger.info("Enriching jurisdictions with political data") | |
| # This would integrate: | |
| # 1. Election results (president, governor) as proxy for party lean | |
| # 2. Voter registration data (where available) | |
| # 3. Census voting statistics | |
| # Placeholder implementation | |
| jurisdictions_df['party_lean'] = None # 'D', 'R', 'Swing', etc. | |
| jurisdictions_df['voter_registration_rate'] = None | |
| jurisdictions_df['voter_turnout_2024'] = None | |
| return jurisdictions_df | |
| class ElectedOfficialsTracker: | |
| """Track elected officials and their party affiliations""" | |
| OPENSTATES_API = "https://v3.openstates.org" | |
| GOOGLE_CIVIC_API = "https://www.googleapis.com/civicinfo/v2" | |
| def __init__( | |
| self, | |
| openstates_api_key: Optional[str] = None, | |
| google_civic_api_key: Optional[str] = None | |
| ): | |
| """ | |
| Initialize tracker | |
| Args: | |
| openstates_api_key: OpenStates API key (https://openstates.org/accounts/profile/) | |
| google_civic_api_key: Google Civic Information API key | |
| """ | |
| self.openstates_api_key = openstates_api_key | |
| self.google_civic_api_key = google_civic_api_key | |
| self.session = requests.Session() | |
| def get_state_legislators(self, state_code: str) -> pd.DataFrame: | |
| """ | |
| Get current state legislators with party affiliations | |
| Uses OpenStates API | |
| Args: | |
| state_code: Two-letter state code | |
| Returns: | |
| DataFrame with legislator information | |
| """ | |
| if not self.openstates_api_key: | |
| logger.warning("OpenStates API key required") | |
| return pd.DataFrame() | |
| url = f"{self.OPENSTATES_API}/people" | |
| headers = {"X-API-KEY": self.openstates_api_key} | |
| params = { | |
| "jurisdiction": state_code.lower(), | |
| "per_page": 100 | |
| } | |
| all_legislators = [] | |
| while True: | |
| response = self.session.get(url, headers=headers, params=params) | |
| response.raise_for_status() | |
| data = response.json() | |
| all_legislators.extend(data.get('results', [])) | |
| # Check for next page | |
| if not data.get('pagination', {}).get('next'): | |
| break | |
| params['page'] = data['pagination'].get('page', 1) + 1 | |
| time.sleep(0.5) | |
| df = pd.DataFrame(all_legislators) | |
| logger.info(f"Found {len(df):,} legislators for {state_code}") | |
| return df | |
| def get_local_officials( | |
| self, | |
| address: str | |
| ) -> Dict: | |
| """ | |
| Get local officials for an address using Google Civic API | |
| Args: | |
| address: Full address or city/state | |
| Returns: | |
| Officials information | |
| """ | |
| if not self.google_civic_api_key: | |
| logger.warning("Google Civic API key required") | |
| return {} | |
| url = f"{self.GOOGLE_CIVIC_API}/representatives" | |
| params = { | |
| "address": address, | |
| "key": self.google_civic_api_key | |
| } | |
| response = self.session.get(url, params=params) | |
| response.raise_for_status() | |
| return response.json() | |
| class PoliticalContextEnricher: | |
| """Enrich nonprofit and jurisdiction data with political context""" | |
| def __init__(self): | |
| pass | |
| def add_political_context_to_nonprofits( | |
| self, | |
| nonprofits_df: pd.DataFrame, | |
| contributions_df: pd.DataFrame, | |
| legislators_df: pd.DataFrame | |
| ) -> pd.DataFrame: | |
| """ | |
| Enrich nonprofit data with political context | |
| Adds: | |
| - Officer political donations | |
| - Local legislator party affiliations | |
| - Jurisdiction political lean | |
| Args: | |
| nonprofits_df: Nonprofit organizations | |
| contributions_df: FEC political contributions | |
| legislators_df: State/local legislators | |
| Returns: | |
| Enriched DataFrame | |
| """ | |
| enriched = nonprofits_df.copy() | |
| # Add political donation flag | |
| if not contributions_df.empty: | |
| politically_active_eins = contributions_df['nonprofit_ein'].unique() | |
| enriched['has_politically_active_leadership'] = ( | |
| enriched['EIN'].isin(politically_active_eins) | |
| ) | |
| # Add local legislator party context | |
| if not legislators_df.empty and 'STATE' in enriched.columns: | |
| # Group legislators by state and party | |
| party_by_state = legislators_df.groupby(['state', 'party']).size().unstack(fill_value=0) | |
| # Calculate party majority for each state | |
| for state in party_by_state.index: | |
| if 'Democratic' in party_by_state.columns and 'Republican' in party_by_state.columns: | |
| dem_count = party_by_state.loc[state, 'Democratic'] | |
| rep_count = party_by_state.loc[state, 'Republican'] | |
| if dem_count > rep_count: | |
| majority = 'Democratic' | |
| elif rep_count > dem_count: | |
| majority = 'Republican' | |
| else: | |
| majority = 'Split' | |
| enriched.loc[enriched['STATE'] == state.upper(), 'state_legislature_control'] = majority | |
| return enriched | |
| def analyze_political_environment( | |
| self, | |
| state_code: str, | |
| nonprofits_df: pd.DataFrame, | |
| contributions_df: pd.DataFrame, | |
| grants_df: pd.DataFrame | |
| ) -> Dict: | |
| """ | |
| Analyze political environment for oral health policy | |
| Args: | |
| state_code: State to analyze | |
| nonprofits_df: Nonprofit organizations | |
| contributions_df: Political contributions | |
| grants_df: Grant awards | |
| Returns: | |
| Analysis summary | |
| """ | |
| analysis = { | |
| 'state': state_code, | |
| 'nonprofit_count': len(nonprofits_df), | |
| 'politically_active_orgs': 0, | |
| 'total_political_donations': 0, | |
| 'total_grants_received': 0, | |
| 'donation_to_grant_ratio': 0 | |
| } | |
| if not contributions_df.empty: | |
| state_contribs = contributions_df[ | |
| contributions_df['contributor_state'] == state_code | |
| ] | |
| analysis['politically_active_orgs'] = len( | |
| state_contribs['nonprofit_ein'].unique() | |
| ) | |
| analysis['total_political_donations'] = ( | |
| state_contribs['contribution_amount'].sum() | |
| ) | |
| if not grants_df.empty: | |
| state_grants = grants_df[grants_df['state'] == state_code] | |
| analysis['total_grants_received'] = ( | |
| state_grants['grant_amount'].sum() | |
| ) | |
| if analysis['total_political_donations'] > 0: | |
| analysis['donation_to_grant_ratio'] = ( | |
| analysis['total_grants_received'] / | |
| analysis['total_political_donations'] | |
| ) | |
| return analysis | |
| def main(): | |
| """Example usage""" | |
| print("Voter Data Integration") | |
| print("=" * 60) | |
| print() | |
| print("Data Sources:") | |
| print("1. State Voter Files: Requires state-by-state requests") | |
| print("2. L2 Political: Commercial license required") | |
| print("3. Aristotle: Commercial license required") | |
| print("4. OpenStates: Free API for legislators (key required)") | |
| print("5. Google Civic API: Free API for officials (key required)") | |
| print() | |
| print("For comprehensive voter data, consider:") | |
| print("- Contacting state election offices") | |
| print("- Commercial vendors for bulk national data") | |
| print("- Academic partnerships (VSEC, CCES)") | |
| print() | |
| print("Free alternatives for aggregated data:") | |
| print("- MIT Election Data Lab") | |
| print("- OpenElections project") | |
| print("- Census voting statistics") | |
| if __name__ == "__main__": | |
| main() | |