Esempio n. 1
0
def load_committee_history(committee_id, cycle=None):
    filters = {"per_page": 1}
    if not cycle:
        # if no cycle parameter given,
        # (1.1)call committee/{committee_id}/history/ under tag:committee
        # set cycle = fallback_cycle
        path = "/committee/" + committee_id + "/history/"
        committee = api_caller.load_first_row_data(path, **filters)
        if committee is None:
            raise Http404()
        cycle = committee.get("last_cycle_has_financial")
        if not cycle:
            # when committees only file F1.fallback_cycle = null
            # set cycle = last_cycle_has_activity
            cycle = committee.get("last_cycle_has_activity")
    else:
        # (1.2)call committee/{committee_id}/history/{cycle}/
        # under tag:committee
        path = "/committee/" + committee_id + "/history/" + str(cycle)
        committee = api_caller.load_first_row_data(path, **filters)
        if committee is None:
            raise Http404()

    # (2)call committee/{committee_id}/candidates/history/{cycle}
    # under: candidate, get all candidates associated with that commitee
    path = "/committee/" + committee_id + "/candidates/history/" + str(cycle)
    all_candidates = api_caller.load_endpoint_results(path,
                                                      election_full=False)

    # clean cycles_has_activity, remove 'None' value in cycles_has_activity
    committee["cycles_has_activity"] = list(
        filter(None, committee.get("cycles_has_activity")))

    return committee, all_candidates, cycle
Esempio n. 2
0
def load_most_recent_candidate(candidate_id):
    """
    Get most recent candidate information
    """
    path = "/candidate/" + candidate_id + "/history/"
    filters = {"per_page": 1}
    return api_caller.load_first_row_data(path, **filters)
Esempio n. 3
0
def load_reports_and_totals(committee_id, cycle, cycle_out_of_range, fallback_cycle):

    filters = {
        "committee_id": committee_id,
        "cycle": fallback_cycle if cycle_out_of_range else cycle,
        "per_page": 1,
        "sort_hide_null": True,
    }

    # (3) call /filings? under tag:filings
    # get reports from filings endpoint filter by form_category=REPORT
    path = "/filings/"
    reports = api_caller.load_first_row_data(
        path, form_category="REPORT", most_recent=True, **filters
    )

    # (4)call committee/{committee_id}/totals? under tag:financial
    # get financial totals
    path = "/committee/" + committee_id + "/totals/"
    totals = api_caller.load_first_row_data(path, **filters)

    return reports, totals
Esempio n. 4
0
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
Esempio n. 5
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. 6
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",
    }