def elections(request, office, cycle, state=None, district=None): cycle = int(cycle) # Get all cycles up until the cycle from the URL if it's beyond the current cycle # this fixes the issue of an election page not showing user-provided cycle # in the cycle select max_cycle = cycle if cycle > utils.current_cycle( ) else utils.current_cycle() cycles = utils.get_cycles(max_cycle) if office.lower() == 'president': cycles = [each for each in cycles if each % 4 == 0] elif office.lower() == 'senate': cycles = api_caller.get_all_senate_cycles(state) if office.lower() not in ['president', 'senate', 'house']: raise Http404() if state and state.upper() not in constants.states: raise Http404() return render( request, 'elections.jinja', { 'office': office, 'office_code': office[0], 'parent': 'data', 'cycle': cycle, 'cycles': cycles, 'state': state, 'state_full': constants.states[state.upper()] if state else None, 'district': district, 'title': utils.election_title(cycle, office, state, district), })
def elections(request, office, cycle, state=None, district=None): cycle = int(cycle) # Get all cycles up until the cycle from the URL if it's beyond the current cycle # this fixes the issue of an election page not showing user-provided cycle # in the cycle select max_cycle = cycle if cycle > utils.current_cycle( ) else utils.current_cycle() cycles = utils.get_cycles(max_cycle) if office.lower() == 'president': cycles = [each for each in cycles if each % 4 == 0] elif office.lower() == 'senate': cycles = api_caller.get_all_senate_cycles(state) if office.lower() not in ['president', 'senate', 'house']: raise Http404() if (state is not None) and (state and state.upper() not in constants.states): raise Http404() #map/redirect legacy tab names to correct anchor tab = request.GET.get('tab', '').replace('/', '') legacy_tabs = { 'contributions': '#individual-contributions', 'totals': '#candidate-financial-totals', 'spending-by-others': '#independent-expenditures' } if tab in legacy_tabs.keys(): if office == 'senate' or office == 'house': return redirect( reverse('elections-state-district', args=(office, state, district, cycle)) + legacy_tabs[tab]) if office == 'president': return redirect( reverse('elections-president', args=(office, cycle)) + legacy_tabs[tab]) return render( request, 'elections.jinja', { 'office': office, 'office_code': office[0], 'parent': 'data', 'cycle': cycle, 'cycles': cycles, 'state': state, 'state_full': constants.states[state.upper()] if state else None, 'district': district, 'title': utils.election_title(cycle, office, state, district), })
def elections_lookup(request): cycles = utils.get_cycles(utils.current_cycle()) return render(request, 'election-lookup.jinja', { 'parent': 'data', 'cycles': cycles })
def raising_spending(request): office = request.GET.get('office', 'P') election_year = int(request.GET.get('election_year', constants.DEFAULT_ELECTION_YEAR)) max_election_year = utils.current_cycle() + 4 election_years = utils.get_cycles(max_election_year) FEATURES = settings.FEATURES return { 'name_field': 'name', 'id_field:': 'candidate_id', 'candidate': 'candidate_id', 'action': 'raised', 'title': 'Raising: by the numbers', 'election_years': election_years, 'election_year': election_year, 'office': office, 'FEATURES': FEATURES }
def spending(request): office = request.GET.get("office", "P") election_year = int( request.GET.get("election_year", constants.DEFAULT_ELECTION_YEAR)) max_election_year = utils.current_cycle() + 4 election_years = utils.get_cycles(max_election_year) return render( request, "spending-bythenumbers.jinja", { "parent": "data", "title": "Spending: by the numbers", "election_years": election_years, "election_year": election_year, "office": office, "social_image_identifier": "data", }, )
def raising(request): top_category = request.GET.get('top_category', 'P') cycles = utils.get_cycles(utils.current_cycle()) cycle = request.GET.get('cycle', constants.DEFAULT_TIME_PERIOD) if top_category in ['pac']: top_raisers = api_caller.load_top_pacs('-receipts', cycle=cycle, per_page=10) elif top_category in ['party']: top_raisers = api_caller.load_top_parties('-receipts', cycle=cycle, per_page=10) else: top_raisers = api_caller.load_top_candidates('-receipts', office=top_category, cycle=cycle, per_page=10) if cycle == datetime.datetime.today().year: coverage_end_date = datetime.datetime.today() else: coverage_end_date = datetime.date(cycle, 12, 31) page_info = top_raisers['pagination'] return render( request, 'raising-breakdown.jinja', { 'parent': 'data', 'title': 'Raising breakdown', 'top_category': top_category, 'coverage_start_date': datetime.date(cycle - 1, 1, 1), 'coverage_end_date': coverage_end_date, 'cycles': cycles, 'cycle': cycle, 'top_raisers': top_raisers['results'], 'page_info': utils.page_info(top_raisers['pagination']) })
def aggregate_totals(request): office = request.GET.get("office", "P") election_year = int( request.GET.get("election_year", constants.DEFAULT_ELECTION_YEAR)) max_election_year = utils.current_cycle() + 4 election_years = utils.get_cycles(max_election_year) FEATURES = settings.FEATURES return render( request, "widgets/aggregate-totals.jinja", { "title": "Aggregate Totals", "election_years": election_years, "election_year": election_year, "office": office, "FEATURES": FEATURES, "social_image_identifier": "data", }, )
def candidate(request, candidate_id): # grab url query string parameters cycle = request.GET.get('cycle', None) election_full = request.GET.get('election_full', True) if cycle is not None: cycle = int(cycle) # set election_full to boolean from passed string variable election_full = bool(strtobool(str(election_full))) candidate, committees, cycle = api_caller.load_with_nested( 'candidate', candidate_id, 'committees', cycle=cycle, cycle_key='two_year_period', election_full=election_full, ) if election_full and cycle and cycle not in candidate['election_years']: next_cycle = next( (year for year in sorted(candidate['election_years']) if year > cycle), max(candidate['election_years']), ) # If the next_cycle is odd set it to whatever the cycle value was # and then set election_full to false # This solves an issue with special elections if next_cycle % 2 > 0: next_cycle = cycle election_full = False candidate, committees, cycle = api_caller.load_with_nested( 'candidate', candidate_id, 'committees', cycle=next_cycle, cycle_key='two_year_period', election_full=election_full, ) election_year = next( (year for year in sorted(candidate['election_years']) if year >= cycle), None, ) result_type = 'candidates' duration = election_durations.get(candidate['office'], 2) min_cycle = cycle - duration if election_full else cycle report_type = report_types.get(candidate['office']) # For JavaScript context_vars = { 'cycles': candidate['cycles'], 'name': candidate['name'], 'cycle': cycle, 'electionFull': election_full, 'candidateID': candidate['candidate_id'] } # In the case of when a presidential or senate candidate has filed # for a future year that's beyond the current cycle, # set a max_cycle var to the current cycle we're in # and when calling the API for totals, set election_full to False. # The max_cycle value is also referenced in the templates for setting # the cycle for itemized tables. Because these are only in 2-year chunks, # the cycle should never be beyond the one we're in. cycles = [ cycle for cycle in candidate['cycles'] if cycle <= utils.current_cycle() ] max_cycle = cycle if cycle <= utils.current_cycle( ) else utils.current_cycle() show_full_election = election_full if cycle <= utils.current_cycle( ) else False # Annotate committees with most recent available cycle aggregate_cycles = (list(range(cycle, cycle - duration, -2)) if election_full else [cycle]) for committee in committees: committee['related_cycle'] = (max( cycle for cycle in aggregate_cycles if cycle in committee['cycles']) if election_full else candidate['two_year_period']) # Group the committees by designation committee_groups = groupby(committees, lambda each: each['designation']) committees_authorized = committee_groups.get( 'P', []) + committee_groups.get('A', []) committee_groups = committee_groups committees_authorized = committees_authorized committee_ids = [ committee['committee_id'] for committee in committees_authorized ] # Get aggregate totals for the financial summary # And pass through the data processing utils aggregate = api_caller.load_candidate_totals( candidate['candidate_id'], cycle=max_cycle, election_full=show_full_election, ) if aggregate: raising_summary = utils.process_raising_data(aggregate) spending_summary = utils.process_spending_data(aggregate) cash_summary = utils.process_cash_data(aggregate) else: raising_summary = None spending_summary = None cash_summary = None aggregate = aggregate # Get totals for the last two-year period of a cycle for showing on # raising and spending tabs two_year_totals = api_caller.load_candidate_totals( candidate['candidate_id'], cycle=max_cycle, election_full=False) # Get the statements of candidacy statement_of_candidacy = api_caller.load_candidate_statement_of_candidacy( candidate['candidate_id'], cycle=cycle) if statement_of_candidacy: for statement in statement_of_candidacy: # convert string to python datetime and parse for readable output statement['receipt_date'] = datetime.datetime.strptime( statement['receipt_date'], '%Y-%m-%dT%H:%M:%S') statement['receipt_date'] = statement['receipt_date'].strftime( '%m/%d/%Y') # Get all the elections elections = sorted( zip(candidate['election_years'], candidate['election_districts']), key=lambda pair: pair[0], reverse=True, ) return render( request, 'candidates-single.jinja', { 'name': candidate['name'], 'cycle': int(cycle), 'office': candidate['office'], 'office_full': candidate['office_full'], 'state': candidate['state'], 'district': candidate['district'], 'candidate_id': candidate_id, 'party_full': candidate['party_full'], 'incumbent_challenge_full': candidate['incumbent_challenge_full'], 'election_year': election_year, 'election_years': candidate['election_years'], 'result_type': result_type, 'duration': duration, 'min_cycle': min_cycle, 'report_type': report_type, 'cycles': cycles, 'max_cycle': max_cycle, 'show_full_election': show_full_election, 'committee_groups': committee_groups, 'committees_authorized': committees_authorized, 'committee_ids': committee_ids, 'raising_summary': raising_summary, 'spending_summary': spending_summary, 'cash_summary': cash_summary, 'aggregate': aggregate, 'two_year_totals': two_year_totals, 'statement_of_candidacy': statement_of_candidacy, 'elections': elections, 'candidate': candidate, 'context_vars': context_vars })
def committee(request, committee_id): # grab url query string parameters cycle = request.GET.get('cycle', None) redirect_to_previous = False if cycle else True committee, candidates, cycle = api_caller.load_with_nested( 'committee', committee_id, 'candidates', cycle) parent = 'data' cycle = int(cycle) year = to_date(committee, cycle) result_type = 'committees' # Link to current cycle if candidate has a corresponding page, else link # without cycle query parameter # See https://github.com/18F/openFEC/issues/1536 for candidate in candidates: election_years = [ election_year for election_year in candidate['election_years'] if election_year - election_durations[candidate['office']] < cycle <= election_year ] candidate['related_cycle'] = max( election_years) if election_years else None # add related candidates a level below financials = api_caller.load_cmte_financials(committee_id, cycle=cycle) report_type = report_types.get(committee['committee_type'], 'pac-party') reports = financials['reports'] totals = financials['totals'] context_vars = { 'cycle': cycle, 'timePeriod': str(int(cycle) - 1) + '–' + str(cycle), 'name': committee['name'], } template_variables = { 'name': committee['name'], 'committee': committee, 'committee_id': committee_id, 'committee_type_full': committee['committee_type_full'], 'committee_type': committee['committee_type'], 'designation_full': committee['designation_full'], 'street_1': committee['street_1'], 'city': committee['city'], 'state': committee['state'], 'zip': committee['zip'], 'treasurer_name': committee['treasurer_name'], 'parent': parent, 'cycle': cycle, 'cycles': committee['cycles'], 'year': year, 'result_type': result_type, 'report_type': report_type, 'reports': reports, 'totals': totals, 'min_receipt_date': utils.three_days_ago(), 'context_vars': context_vars, 'party_full': committee['party_full'] } if financials['reports'] and financials['totals']: # Format the current two-year-period's totals using the process utilities if committee['committee_type'] == 'I': # IE-only committees have very little data, so they just get this one template_variables['ie_summary'] = utils.process_ie_data( financials['totals'][0]) else: # All other committees have three tables template_variables['raising_summary'] = utils.process_raising_data( financials['totals'][0]) template_variables[ 'spending_summary'] = utils.process_spending_data( financials['totals'][0]) template_variables['cash_summary'] = utils.process_cash_data( financials['totals'][0]) if redirect_to_previous and not financials['reports']: # If there's no reports, find the first year with reports and redirect there for c in sorted(committee['cycles'], reverse=True): financials = api_caller.load_cmte_financials( committee['committee_id'], cycle=c) if financials['reports']: return redirect( reverse('committee-by-id', kwargs={'committee_id': committee['committee_id']}) + '?cycle=' + str(c)) # If it's not a senate committee and we're in the current cycle # check if there's any raw filings in the last three days if committee['committee_type'] != 'S' and cycle == utils.current_cycle(): raw_filings = api_caller._call_api( 'efile', 'filings', cycle=cycle, committee_id=committee['committee_id'], min_receipt_date=template_variables['min_receipt_date']) if len(raw_filings.get('results')) > 0: template_variables['has_raw_filings'] = True else: template_variables['has_raw_filings'] = False return render(request, 'committees-single.jinja', template_variables)
def elections(request, office, cycle, state=None, district=None): cycle = int(cycle) max_cycle = utils.current_cycle() + 4 cycles = utils.get_cycles(max_cycle) if office.lower() == "president": cycles = [each for each in cycles if each % 4 == 0] elif office.lower() == "senate": cycles = api_caller.get_all_senate_cycles(state) if office.lower() not in ["president", "senate", "house"]: raise Http404() if (state is not None) and (state and state.upper() not in constants.states): raise Http404() election_duration = election_durations.get(office[0].upper(), 2) # Puerto Rico house/resident commissioners have 4-year cycles if state and state.upper() == "PR": election_duration = 4 # map/redirect legacy tab names to correct anchor tab = request.GET.get("tab", "").replace("/", "") legacy_tabs = { "contributions": "#individual-contributions", "totals": "#candidate-financial-totals", "spending-by-others": "#independent-expenditures", } if tab in legacy_tabs: if office == "house": return redirect( reverse("elections-house", args=(office, state, district, cycle)) + legacy_tabs[tab]) elif office == "senate": return redirect( reverse("elections-senate", args=(office, state, cycle)) + legacy_tabs[tab]) elif office == "president": return redirect( reverse("elections-president", args=(office, cycle)) + legacy_tabs[tab]) return render( request, "elections.jinja", { "office": office, "office_code": office[0], "parent": "data", "cycle": cycle, "election_duration": election_duration, "cycles": cycles, "state": state, "state_full": constants.states[state.upper()] if state else None, "district": district, "title": utils.election_title(cycle, office, state, district), "social_image_identifier": "data", }, )
def get_committee(committee_id, cycle): """ Given a committee_id and cycle, call the API and get the committee and committee financial data needed to render the committee profile page """ committee, all_candidates, cycle = load_committee_history( committee_id, cycle) # When there are multiple candidate records of various offices (H, S, P) # linked to a single committee ID, # associate the candidate record with the matching committee type candidates = [ candidate for candidate in all_candidates if committee["committee_type"] == candidate["office"] ] parent = "data" result_type = "committees" cycle = int(cycle) year = to_date(committee, cycle) # Link to current cycle if candidate has a corresponding page, else link # without cycle query parameter # See https://github.com/fecgov/openFEC/issues/1536 for candidate in candidates: election_years = [] for election_year in candidate["election_years"]: start_of_election_period = ( election_year - election_durations[candidate["office"]]) if start_of_election_period < cycle and cycle <= election_year: election_years.append(election_year) # For each candidate, set related_cycle to the candidate's time period # relative to the selected cycle. candidate["related_cycle"] = cycle if election_years else None report_type = report_types.get(committee["committee_type"], "pac-party") cycle_out_of_range, fallback_cycle, cycles = load_cycle_data( committee, cycle) reports, totals = load_reports_and_totals(committee_id, cycle, cycle_out_of_range, fallback_cycle) # Check organization types to determine SSF status is_ssf = committee.get("organization_type") in [ "W", "C", "L", "V", "M", "T" ] # if cycles_has_activity's options are more than cycles_has_financial's, # when clicking back to financial summary/raising/spending, # reset cycle=fallback_cycle and timePeriod and. # to make sure missing message page show correct timePeriod. time_period_js = str(int(cycle) - 1) + "–" + str(cycle) cycle_js = cycle if cycle_out_of_range: cycle_js = fallback_cycle time_period_js = str(int(cycle_js) - 1) + "–" + str(cycle_js) context_vars = { "cycle": cycle_js, "timePeriod": time_period_js, "name": committee["name"], "cycleOutOfRange": cycle_out_of_range, "lastCycleHasFinancial": fallback_cycle, } template_variables = { "candidates": candidates, "committee": committee, "committee_id": committee_id, "committee_type": committee["committee_type"], "context_vars": context_vars, "cycle": cycle, "cycles": cycles, "is_SSF": is_ssf, "cycle_out_of_range": cycle_out_of_range, "parent": parent, "result_type": result_type, "report_type": report_type, "reports": reports, "totals": totals, "min_receipt_date": utils.three_days_ago(), "context_vars": context_vars, "party_full": committee["party_full"], "candidates": candidates, "social_image_identifier": "data", "year": year, "timePeriod": time_period_js, } # Format the current two-year-period's totals if reports and totals: # IE-only committees if committee["committee_type"] == "I": template_variables["ie_summary"] = utils.process_ie_data(totals) # Inaugural Committees elif committee["organization_type"] == "I": template_variables[ "inaugural_summary"] = utils.process_inaugural_data(totals) # Host Committees that file on Form 4 elif committee["organization_type"] == "H" and reports[ "form_type"] == "F4": template_variables[ "raising_summary"] = utils.process_host_raising_data(totals) template_variables[ "spending_summary"] = utils.process_host_spending_data(totals) template_variables["cash_summary"] = utils.process_cash_data( totals) else: # All other committees have three tables template_variables["raising_summary"] = utils.process_raising_data( totals) template_variables[ "spending_summary"] = utils.process_spending_data(totals) template_variables["cash_summary"] = utils.process_cash_data( totals) # If in the current cycle, check for raw filings in the last three days if cycle == utils.current_cycle(): # (4)call efile/filings under tag: efiling path = "/efile/filings/" filters = {} filters["cycle"] = cycle filters["committee_id"] = committee["committee_id"] filters["min_receipt_date"] = template_variables["min_receipt_date"] raw_filings = api_caller.load_endpoint_results(path, **filters) template_variables["has_raw_filings"] = True if raw_filings else False else: template_variables["has_raw_filings"] = False # Needed for filings tab template_variables["filings_lookup"] = { "reports": ["F3", "F3X", "F3P", "F3L", "F4", "F5", "F7", "F13"], "notices": ["F5", "F24", "F6", "F9", "F10", "F11"], "statements": ["F1"], "other": ["F1M", "F8", "F99", "F12"], } # Call /filings?committee_id=C00693234&form_type=F1 # Get the statements of organization statement_of_organization = api_caller.load_committee_statement_of_organization( committee_id) if statement_of_organization: statement_of_organization["receipt_date"] = format_receipt_date( statement_of_organization["receipt_date"]) template_variables["statement_of_organization"] = statement_of_organization # Add message for a committee that was formerly an authorized candidate committee. # These committees are now unauthorized committees. converted_committee_id = None former_committee_name = None former_authorized_candidate_id = None former_authorized_candidate_name = None if cycle == 2020 and committee_to_candidate_linkage.get(committee_id): # Call /candidate/{candidate_id}/history/ converted_committee_id = committee.get('committee_id') former_authorized_candidate_id = committee_to_candidate_linkage.get( converted_committee_id) path = "/candidate/" + str( former_authorized_candidate_id) + "/history/" filters = {} filters["per_page"] = 1 candidate = api_caller.load_first_row_data(path, **filters) # Get the converted committee's former name and candidate name former_committee_name = former_committee_names.get( converted_committee_id) former_authorized_candidate_name = candidate.get('name') template_variables["former_committee_name"] = former_committee_name template_variables[ "former_authorized_candidate_name"] = former_authorized_candidate_name template_variables[ "former_authorized_candidate_id"] = former_authorized_candidate_id return template_variables