Esempio n. 1
0
def get_candidate(candidate_id, cycle, election_full):
    """
    1) By passing parameter "candidate_id" to get candidate data.
    2) "cycle" and "election_full" are optional parameters.
       if no election_full, election_full = true.
       if no cycle, cycle = the latest item in rounded_election_years.
    3) totally call 5-6 endpoints.
    """

    # (1)call candidate/{candidate_id}/history/ under tag:candidate
    # get rounded_election_years(candidate_election_year).
    path = "/candidate/" + candidate_id + "/history/"
    filters = {}
    filters["per_page"] = 1
    candidate = api_caller.load_first_row_data(path, **filters)
    if candidate is None:
        raise Http404()

    # a)Build List: even_election_years for candidate election dropdown list
    #   on candidate profile page.
    # b)rounded_election_years round up any odd year special election_year
    #   to even number. (ex:2017=>2018)
    # even_election_years = candidate.get('rounded_election_years')

    # if cycle = null, set cycle = the last of rounded_election_years.
    if not cycle:
        cycle = max(candidate.get("rounded_election_years"))

    # (2)call candidate/{candidate_id}/history/{cycle} under tag:candidate
    # (3)call candidate/{candidate_id}/committees/history/{cycle}
    # under tag:committee.
    candidate, committees, cycle = api_caller.load_with_nested(
        "candidate",
        candidate_id,
        "committees",
        cycle=cycle,
        election_full=election_full,
    )

    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["fec_cycles_in_election"],
        "name": candidate["name"],
        "cycle": cycle,
        "electionFull": election_full,
        "candidateID": candidate["candidate_id"],
    }

    max_cycle = int(cycle)
    # Check if cycle > latest_fec_cycles_in_election, such as:future candidate.
    if candidate["fec_cycles_in_election"]:
        latest_fec_cycles_in_election = max(
            candidate["fec_cycles_in_election"])
        if int(cycle) > latest_fec_cycles_in_election:
            max_cycle = latest_fec_cycles_in_election

    # 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"] = committee["cycle"]

    # Group the committees by designation
    committee_groups = groupby(committees, lambda each: each["designation"])
    committees_authorized = committee_groups.get(
        "P", []) + committee_groups.get("A", [])

    committee_ids = [
        committee["committee_id"] for committee in committees_authorized
    ]

    # (4)call candidate/{candidate_id}/totals/{cycle} under tag:candidate
    # Get aggregate totals for the financial summary
    filters["election_full"] = election_full
    filters["cycle"] = cycle
    path = "/candidate/" + candidate_id + "/totals/"
    aggregate = api_caller.load_first_row_data(path, **filters)

    if election_full:
        # (5)if election_full is true, need call
        # candidate/{candidate_id}/totals/{cycle} second time
        # for showing on raising and spending tabs
        # Get most recent 2-year period totals
        filters["election_full"] = False
        filters["cycle"] = max_cycle
        two_year_totals = api_caller.load_first_row_data(path, **filters)
    else:
        two_year_totals = aggregate

    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

    # (6)Call /filings?candidate_id=P00003392&form_type=F2
    # Get the statements of candidacy
    statement_of_candidacy = api_caller.load_candidate_statement_of_candidacy(
        candidate["candidate_id"])

    if statement_of_candidacy:
        statement_of_candidacy["receipt_date"] = format_receipt_date(
            statement_of_candidacy["receipt_date"])

    # Get all the elections
    elections = sorted(
        zip(candidate["election_years"], candidate["election_districts"]),
        key=lambda pair: pair[0],
        reverse=True,
    )

    # (7)call efile/filings/ under tag:efiling
    # Check if there are raw_filings for this candidate
    raw_filing_start_date = utils.three_days_ago()
    filters["min_receipt_date"] = raw_filing_start_date
    filters["committee_id"] = candidate["candidate_id"]
    filters["cycle"] = cycle
    filters["per_page"] = 100
    path = "/efile/" + "/filings/"
    raw_filings = api_caller.load_endpoint_results(path, **filters)
    has_raw_filings = True if raw_filings else False

    # Add message for when a candidate converts their candidate committee to an unauthorized committee
    current_committee_name = None
    converted_committee_id = None
    former_committee_name = None

    if cycle == 2020 and candidate_to_committee_linkage.get(candidate_id):
        # Call committee/{committee_id}/history/{cycle}/
        filters = {"per_page": 1}
        path = "/committee/" + candidate_to_committee_linkage.get(
            candidate_id) + "/history/" + str(cycle)
        committee = api_caller.load_first_row_data(path, **filters)
        # Get the converted committee's name, committee ID, and former committee name
        current_committee_name = committee.get('name')
        converted_committee_id = committee.get('committee_id')
        former_committee_name = former_committee_names.get(
            converted_committee_id)

    return {
        "converted_committee_name": current_committee_name,
        "converted_committee_id": converted_committee_id,
        "former_committee_name": former_committee_name,
        "aggregate": aggregate,
        "aggregate_cycles": aggregate_cycles,
        "candidate": candidate,
        "candidate_id": candidate_id,
        "cash_summary": cash_summary,
        "committee_groups": committee_groups,
        "committee_ids": committee_ids,
        # filings endpoint takes candidate ID value as committee ID arg
        "committee_id": candidate["candidate_id"],
        "committees_authorized": committees_authorized,
        "context_vars": context_vars,
        "cycle": int(cycle),
        "cycles": candidate["fec_cycles_in_election"],
        "district": candidate["district"],
        "duration": duration,
        "election_year": cycle,
        "election_years": candidate.get("rounded_election_years"),
        "elections": elections,
        "has_raw_filings": has_raw_filings,
        "incumbent_challenge_full": candidate["incumbent_challenge_full"],
        "max_cycle": max_cycle,
        "min_cycle": min_cycle,
        "min_receipt_date": raw_filing_start_date,
        "name": candidate["name"],
        "office": candidate["office"],
        "office_full": candidate["office_full"],
        "party_full": candidate["party_full"],
        "raising_summary": raising_summary,
        "report_type": report_type,
        "result_type": result_type,
        "show_full_election": election_full,
        "spending_summary": spending_summary,
        "state": candidate["state"],
        "statement_of_candidacy": statement_of_candidacy,
        "two_year_totals": two_year_totals,
        "social_image_identifier": "data",
    }
Esempio n. 2
0
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
        })
Esempio n. 3
0
def get_candidate(candidate_id, cycle, election_full):
    """
    1) By passing parameter "candidate_id" to get candidate data.
    2) "cycle" and "election_full" are optional parameters.
       if no election_full, election_full = true.
       if no cycle, cycle = the latest item in rounded_election_years.
    3) totally call 5-6 endpoints.
    """

    # (1) Call candidate/{candidate_id}/history/ under tag:candidate
    # get rounded_election_years(candidate_election_year).
    candidate = load_most_recent_candidate(candidate_id)
    if candidate is None:
        raise Http404()

    # if cycle = null, set cycle = the last of rounded_election_years.
    if not cycle:
        cycle = max(candidate.get("rounded_election_years"))

    # (2) Call candidate/{candidate_id}/history/{cycle} under tag:candidate
    # (3) Call candidate/{candidate_id}/committees/history/{cycle}
    # under tag:committee.
    candidate, committees, cycle = api_caller.load_with_nested(
        "candidate",
        candidate_id,
        "committees",
        cycle=cycle,
        election_full=election_full,
    )

    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["fec_cycles_in_election"],
        "name": candidate["name"],
        "cycle": cycle,
        "electionFull": election_full,
        "candidateID": candidate["candidate_id"],
    }

    max_cycle = int(cycle)
    # Check if cycle > latest_fec_cycles_in_election, such as:future candidate.
    if candidate["fec_cycles_in_election"]:
        latest_fec_cycles_in_election = max(
            candidate["fec_cycles_in_election"])
        if int(cycle) > latest_fec_cycles_in_election:
            max_cycle = latest_fec_cycles_in_election

    # 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"] = committee["cycle"]

    # Group the committees by designation
    committee_groups = groupby(committees, lambda each: each["designation"])
    committees_authorized = committee_groups.get(
        "P", []) + committee_groups.get("A", [])
    committee_ids = [
        committee["committee_id"] for committee in committees_authorized
    ]

    # Group the committees by leadership pac (designation=D)
    committees_d = committee_groups.get("D", [])
    committees_leadership_pac = []
    if committees_d:
        # The candidate(id=P00009183) returns two rows from api result,
        # one is pcc converted to D, another is leadership pac committee,
        # remove the duplicate committees with same committees_id.
        # example api call:
        # https://fec-dev-api.app.cloud.gov/v1/candidate/P00009183/committees/history/2020/
        len_d_cmte = len(committees_d)
        committees_leadership_pac.append(committees_d[0])
        for i in range(len_d_cmte):
            if (i + 1) < len_d_cmte and committees_d[i].get(
                    "committee_id") != committees_d[i + 1].get("committee_id"):
                committees_leadership_pac.append(committees_d[i + 1])

    # (4) Call candidate/{candidate_id}/totals/{cycle} under tag:candidate
    # Get aggregate totals for the financial summary
    filters = {"election_full": election_full, "cycle": cycle}
    path = "/candidate/" + candidate_id + "/totals/"
    aggregate = api_caller.load_first_row_data(path, **filters)

    if election_full:
        # (5) if election_full is true, need call
        # candidate/{candidate_id}/totals/{cycle} second time
        # for showing on raising and spending tabs
        # Get most recent 2-year period totals
        filters = {"election_full": False, "cycle": max_cycle}
        two_year_totals = api_caller.load_first_row_data(path, **filters)
    else:
        two_year_totals = aggregate

    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

    # (6) Call /filings?candidate_id=P00003392&form_type=F2
    # Get the statements of candidacy
    statement_of_candidacy = api_caller.load_candidate_statement_of_candidacy(
        candidate["candidate_id"])

    if statement_of_candidacy:
        statement_of_candidacy["receipt_date"] = format_receipt_date(
            statement_of_candidacy["receipt_date"])

    # Get all the elections
    elections = sorted(
        zip(candidate["election_years"], candidate["election_districts"]),
        key=lambda pair: pair[0],
        reverse=True,
    )

    # (7) Call efile/filings/ under tag:efiling
    # Check if there are raw_filings for this candidate
    raw_filing_start_date = utils.three_days_ago()
    filters = {
        "min_receipt_date": raw_filing_start_date,
        "committee_id": candidate["candidate_id"],
        "cycle": cycle,
        "per_page": 100,
    }
    path = "/efile/" + "/filings/"
    raw_filings = api_caller.load_endpoint_results(path, **filters)
    has_raw_filings = True if raw_filings else False

    # Add message for when a candidate converts their candidate committee to an unauthorized committee
    current_committee_name = None
    converted_committee_id = None
    former_committee_name = None

    # Get the latest committee name, former authorized committee name, and committee ID.
    # This will be the first item returned in the committees list
    # If there are no committees, return the normal no results message
    if len(committees) > 0:
        if committees[0].get('former_candidate_id'):
            current_committee_name = committees[0].get('name')
            converted_committee_id = committees[0].get('committee_id')
            former_committee_name = committees[0].get('former_committee_name')

    return {
        "converted_committee_name": current_committee_name,
        "converted_committee_id": converted_committee_id,
        "former_committee_name": former_committee_name,
        "aggregate": aggregate,
        "aggregate_cycles": aggregate_cycles,
        "candidate": candidate,
        "candidate_id": candidate_id,
        "cash_summary": cash_summary,
        "committee_groups": committee_groups,
        "committee_ids": committee_ids,
        # filings endpoint takes candidate ID value as committee ID arg
        "committee_id": candidate["candidate_id"],
        "committees_authorized": committees_authorized,
        "committees_leadership_pac": committees_leadership_pac,
        "context_vars": context_vars,
        "cycle": int(cycle),
        "cycles": candidate["fec_cycles_in_election"],
        "district": candidate["district"],
        "duration": duration,
        "election_year": cycle,
        "election_years": candidate.get("rounded_election_years"),
        "elections": elections,
        "has_raw_filings": has_raw_filings,
        "incumbent_challenge_full": candidate["incumbent_challenge_full"],
        "max_cycle": max_cycle,
        "min_cycle": min_cycle,
        "min_receipt_date": raw_filing_start_date,
        "name": candidate["name"],
        "office": candidate["office"],
        "office_full": candidate["office_full"],
        "party_full": candidate["party_full"],
        "raising_summary": raising_summary,
        "report_type": report_type,
        "result_type": result_type,
        "show_full_election": election_full,
        "spending_summary": spending_summary,
        "state": candidate["state"],
        "statement_of_candidacy": statement_of_candidacy,
        "two_year_totals": two_year_totals,
        "social_image_identifier": "data",
    }