Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
File size: 11,766 Bytes
61d29fc | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 | """
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()
|