Ejemplo n.º 1
0
def import_maplight_from_json(request):
    load_from_url = False
    ballot_for_one_voter_array = []
    if load_from_url:
        # Request json file from Maplight servers
        logger.debug("TO BE IMPLEMENTED: Load Maplight JSON from url")
        # request = requests.get(VOTER_INFO_URL, params={
        #     "key": GOOGLE_CIVIC_API_KEY,  # This comes from an environment variable
        #     "address": "254 Hartford Street San Francisco CA",
        #     "electionId": "2000",
        # })
        # structured_json = json.loads(request.text)
    else:
        # Load saved json from local file
        logger.debug("Loading Maplight sample JSON from local file")

        with open(
                MAPLIGHT_SAMPLE_BALLOT_JSON_FILE) as ballot_for_one_voter_json:
            ballot_for_one_voter_array = json.load(ballot_for_one_voter_json)

    # A MapLight ballot query is essentially an array of contests with the key as the contest_id
    if ballot_for_one_voter_array and len(ballot_for_one_voter_array):
        # Parse the JSON here. This JSON is a list of contests on the ballot for one voter.
        for contest_id in ballot_for_one_voter_array:
            # Get a description of the contest. Office? Measure?
            contest_overview_array = ballot_for_one_voter_array[contest_id]

            if contest_overview_array['type'] == "office":
                # Get a description of the office the candidates are competing for
                # contest_office_description_json = contest_overview_array['office']

                # With the contest_id, we can look up who is running
                politicians_running_for_one_contest_array = []
                if load_from_url:
                    logger.debug(
                        "TO BE IMPLEMENTED: Load MapLight JSON for a contest from URL"
                    )
                else:
                    json_file_with_the_data_from_this_contest = MAPLIGHT_SAMPLE_CONTEST_JSON_FILE.format(
                        contest_id=contest_id)
                    try:
                        with open(json_file_with_the_data_from_this_contest
                                  ) as json_data:
                            politicians_running_for_one_contest_array = json.load(
                                json_data)
                    except Exception as e:
                        logger.error("File {file_path} not found.".format(
                            file_path=json_file_with_the_data_from_this_contest
                        ))
                        # Don't try to process the file if it doesn't exist, but go to the next entry
                        continue

                import_maplight_contest_office_candidates_from_array(
                    politicians_running_for_one_contest_array)

            # Also add measure
    return
Ejemplo n.º 2
0
def process_contest_referendum_from_structured_json(
        one_contest_referendum_structured_json, google_civic_election_id, local_ballot_order, save_to_db):
    """
    "referendumTitle": "Proposition 45",
    "referendumSubtitle": "Healthcare Insurance. Rate Changes. Initiative Statute.",
    "referendumUrl": "http://vig.cdn.sos.ca.gov/2014/general/en/pdf/proposition-45-title-summary-analysis.pdf",
    "district" <= this is an array
    """
    logger.debug("Referendum contest_type")
    referendum_title = one_contest_referendum_structured_json['referendumTitle']
    referendum_subtitle = one_contest_referendum_structured_json['referendumSubtitle']
    referendum_url = one_contest_referendum_structured_json['referendumUrl']

    # These following fields exist for both candidates and referendum
    results = process_contest_common_fields_from_structured_json(one_contest_referendum_structured_json)
    ballot_placement = results['ballot_placement']  # A number specifying the position of this contest
    # on the voter's ballot.
    primary_party = results['primary_party']  # If this is a partisan election, the name of the party it is for.
    district_name = results['district_name']  # The name of the district.
    district_scope = results['district_scope']   # The geographic scope of this district. If unspecified the
    # district's geography is not known. One of: national, statewide, congressional, stateUpper, stateLower,
    # countywide, judicial, schoolBoard, cityWide, township, countyCouncil, cityCouncil, ward, special
    district_ocd_id = results['district_ocd_id']
    electorate_specifications = results['electorate_specifications']  # A description of any additional
    #  eligibility requirements for voting in this contest.
    special = results['special']  # "Yes" or "No" depending on whether this a contest being held
    # outside the normal election cycle.

    if save_to_db:
        if referendum_title and referendum_subtitle and district_name and district_scope and district_ocd_id \
                and google_civic_election_id:
            try:
                query1 = GoogleCivicContestReferendum.objects.all()
                query1 = query1.filter(referendum_title__exact=referendum_title)
                query1 = query1.filter(google_civic_election_id__exact=google_civic_election_id)
                query1 = query1.filter(district_scope__exact=district_scope)

                # Was at least one existing entry found based on the above criteria?
                if len(query1):
                    google_civic_contest_referendum = query1[0]
                # If no entries found previously, create a new entry
                else:
                    google_civic_contest_referendum = \
                        GoogleCivicContestReferendum.objects.create(referendum_title=referendum_title,
                                                                    referendum_subtitle=referendum_subtitle,
                                                                    google_civic_election_id=google_civic_election_id,
                                                                    referendum_url=referendum_url,
                                                                    ballot_placement=ballot_placement,
                                                                    primary_party=primary_party,
                                                                    district_name=district_name,
                                                                    district_scope=district_scope,
                                                                    district_ocd_id=district_ocd_id,
                                                                    electorate_specifications=electorate_specifications,
                                                                    special=special,
                                                                    )

                # Save information about this contest item on the voter's ballot from: ballot_placement
            except Exception as e:
                handle_record_not_found_exception(e, logger=logger)

    return
Ejemplo n.º 3
0
def process_contest_office_from_structured_json(
        one_contest_office_structured_json, google_civic_election_id, local_ballot_order, save_to_db):
    voter_id = 1  # TODO Temp

    logger.debug("General contest_type")
    office = one_contest_office_structured_json['office']

    # The number of candidates that a voter may vote for in this contest.
    if 'numberVotingFor' in one_contest_office_structured_json:
        number_voting_for = one_contest_office_structured_json['numberVotingFor']
    else:
        number_voting_for = 1

    # The number of candidates that will be elected to office in this contest.
    if 'numberElected' in one_contest_office_structured_json:
        number_elected = one_contest_office_structured_json['numberElected']
    else:
        number_elected = 1

    results = process_contest_common_fields_from_structured_json(one_contest_office_structured_json)
    ballot_placement = results['ballot_placement']  # A number specifying the position of this contest
    # on the voter's ballot.
    primary_party = results['primary_party']  # If this is a partisan election, the name of the party it is for.
    district_name = results['district_name']  # The name of the district.
    district_scope = results['district_scope']   # The geographic scope of this district. If unspecified the
    # district's geography is not known. One of: national, statewide, congressional, stateUpper, stateLower,
    # countywide, judicial, schoolBoard, cityWide, township, countyCouncil, cityCouncil, ward, special
    district_ocd_id = results['district_ocd_id']
    electorate_specifications = results['electorate_specifications']  # A description of any additional
    # eligibility requirements for voting in this contest.
    special = results['special']  # "Yes" or "No" depending on whether this a contest being held
    # outside the normal election cycle.

    # We want to convert this from an array to three fields for the same table
    # levels: string, A list of office levels to filter by. Only offices that serve at least one of these levels
    # will be returned. Divisions that don't contain a matching office will not be returned. (repeated)
    # Allowed values
    #   administrativeArea1 -
    #   administrativeArea2 -
    #   country -
    #   international -
    #   locality -
    #   regional -
    #   special -
    #   subLocality1 -
    #   subLocality2 -
    # The levels of government of the office for this contest. There may be more than one in cases where a
    # jurisdiction effectively acts at two different levels of government; for example, the mayor of the
    # District of Columbia acts at "locality" level, but also effectively at both "administrative-area-2"
    # and "administrative-area-1".
    level_structured_json = one_contest_office_structured_json['level']
    contest_level = []
    for one_level in level_structured_json:
        contest_level.append(one_level)
    if 0 in contest_level:
        contest_level0 = contest_level[0]
    else:
        contest_level0 = ''
    if 1 in contest_level:
        contest_level1 = contest_level[1]
    else:
        contest_level1 = ''
    if 2 in contest_level:
        contest_level2 = contest_level[2]
    else:
        contest_level2 = ''

    # roles: string, A list of office roles to filter by. Only offices fulfilling one of these roles will be returned.
    # Divisions that don't contain a matching office will not be returned. (repeated)
    # Allowed values
    #   deputyHeadOfGovernment -
    #   executiveCouncil -
    #   governmentOfficer -
    #   headOfGovernment -
    #   headOfState -
    #   highestCourtJudge -
    #   judge -
    #   legislatorLowerBody -
    #   legislatorUpperBody -
    #   schoolBoard -
    #   specialPurposeOfficer -
    roles_structured_json = one_contest_office_structured_json['roles']
    # for one_role in roles_structured_json:
    # Figure out how we are going to use level info

    candidates = one_contest_office_structured_json['candidates']

    internal_contest_office_id = 0  # Set to 0 in case a new one is not created
    # Note that all of the information saved here is independent of a particular voter
    if save_to_db:
        if office and district_name and district_scope and district_ocd_id and google_civic_election_id:
            try:
                # Try to find earlier version based on name of the office google_civic_election_id
                query1 = GoogleCivicContestOffice.objects.all()
                query1 = query1.filter(google_civic_election_id__exact=google_civic_election_id)
                query1 = query1.filter(district_scope__exact=district_scope)
                query1 = query1.filter(office__exact=office)

                # Was at least one existing entry found based on the above criteria?
                if len(query1):
                    google_civic_contest_office = query1[0]
                # If no entries found previously, create a new entry
                else:
                    google_civic_contest_office = \
                        GoogleCivicContestOffice.objects.create(office=office,
                                                                google_civic_election_id=google_civic_election_id,
                                                                number_voting_for=number_voting_for,
                                                                number_elected=number_elected,
                                                                contest_level0=contest_level0,
                                                                contest_level1=contest_level1,
                                                                contest_level2=contest_level2,
                                                                ballot_placement=ballot_placement,
                                                                primary_party=primary_party,
                                                                district_name=district_name,
                                                                district_scope=district_scope,
                                                                district_ocd_id=district_ocd_id,
                                                                electorate_specifications=electorate_specifications,
                                                                special=special,
                                                                )
                # The internal id is needed since there isn't a ContestOffice google identifier
                internal_contest_office_id = google_civic_contest_office.id
            except Exception as e:
                handle_record_not_found_exception(e, logger=logger)

        if value_exists(voter_id) and value_exists(google_civic_election_id) and value_exists(district_ocd_id):
            google_civic_ballot_item_manager = GoogleCivicBallotItemManager()
            google_civic_ballot_item_manager.save_ballot_item_for_voter(
                voter_id, google_civic_election_id, district_ocd_id, ballot_placement, local_ballot_order)

    process_candidates_from_structured_json(
        candidates, google_civic_election_id, internal_contest_office_id, save_to_db)

    return
Ejemplo n.º 4
0
def process_contest_office_from_structured_json(
        one_contest_office_structured_json, google_civic_election_id, ocd_division_id, local_ballot_order, state_code,
        voter_id):
    logger.debug("General contest_type")

    # Protect against the case where this is NOT an office
    if 'candidates' not in one_contest_office_structured_json:
        is_not_office = True
    else:
        is_not_office = False
    if is_not_office:
        update_or_create_contest_office_results = {
            'success': False,
            'saved': 0,
            'updated': 0,
            'not_processed': 1,
        }
        return update_or_create_contest_office_results

    office_name = one_contest_office_structured_json['office']

    # The number of candidates that a voter may vote for in this contest.
    if 'numberVotingFor' in one_contest_office_structured_json:
        number_voting_for = one_contest_office_structured_json['numberVotingFor']
    else:
        number_voting_for = 1

    # The number of candidates that will be elected to office in this contest.
    if 'numberElected' in one_contest_office_structured_json:
        number_elected = one_contest_office_structured_json['numberElected']
    else:
        number_elected = 1

    # These are several fields that are shared in common between offices and measures
    results = process_contest_common_fields_from_structured_json(one_contest_office_structured_json)

    # ballot_placement: A number specifying the position of this contest on the voter's ballot.
    google_ballot_placement = results['ballot_placement']
    primary_party = results['primary_party']  # If this is a partisan election, the name of the party it is for.

    # district_scope: The geographic scope of this district. If unspecified the
    # district's geography is not known. One of: national, statewide, congressional, stateUpper, stateLower,
    # countywide, judicial, schoolBoard, cityWide, township, countyCouncil, cityCouncil, ward, special
    district_scope = results['district_scope']
    district_id = results['district_id']
    district_name = results['district_name']  # The name of the district.

    # electorate_specifications: A description of any additional eligibility requirements for voting in this contest.
    electorate_specifications = results['electorate_specifications']

    # special: "Yes" or "No" depending on whether this a contest being held outside the normal election cycle.
    special = results['special']

    # We want to convert this from an array to three fields for the same table
    # levels: string, A list of office levels to filter by. Only offices that serve at least one of these levels
    # will be returned. Divisions that don't contain a matching office will not be returned. (repeated)
    # Allowed values
    #   administrativeArea1 -
    #   administrativeArea2 -
    #   country -
    #   international -
    #   locality -
    #   regional -
    #   special -
    #   subLocality1 -
    #   subLocality2 -
    # The levels of government of the office for this contest. There may be more than one in cases where a
    # jurisdiction effectively acts at two different levels of government; for example, the mayor of the
    # District of Columbia acts at "locality" level, but also effectively at both "administrative-area-2"
    # and "administrative-area-1".
    level_structured_json = \
        one_contest_office_structured_json['level'] if 'level' in one_contest_office_structured_json else ''
    contest_level = []
    for one_level in level_structured_json:
        contest_level.append(one_level)
    if 0 in contest_level:
        contest_level0 = contest_level[0]
    else:
        contest_level0 = ''
    if 1 in contest_level:
        contest_level1 = contest_level[1]
    else:
        contest_level1 = ''
    if 2 in contest_level:
        contest_level2 = contest_level[2]
    else:
        contest_level2 = ''

    # roles: string, A list of office roles to filter by. Only offices fulfilling one of these roles will be returned.
    # Divisions that don't contain a matching office will not be returned. (repeated)
    # Allowed values
    #   deputyHeadOfGovernment -
    #   executiveCouncil -
    #   governmentOfficer -
    #   headOfGovernment -
    #   headOfState -
    #   highestCourtJudge -
    #   judge -
    #   legislatorLowerBody -
    #   legislatorUpperBody -
    #   schoolBoard -
    #   specialPurposeOfficer -
    # roles_structured_json = \
    #     one_contest_office_structured_json['roles'] if 'roles' in one_contest_office_structured_json else ''
    # for one_role in roles_structured_json:
    # Figure out how we are going to use level info

    candidates_structured_json = \
        one_contest_office_structured_json['candidates'] if 'candidates' in one_contest_office_structured_json else ''

    we_vote_id = ''
    # Note that all of the information saved here is independent of a particular voter
    if google_civic_election_id and (district_id or district_name) and office_name:
        updated_contest_office_values = {
            # Values we search against
            'google_civic_election_id': google_civic_election_id,
            'state_code': state_code.lower(),  # Not required for cases of federal offices
            'district_id': district_id,
            'district_name': district_name,
            'office_name': office_name,
            # The rest of the values
            'ocd_division_id': ocd_division_id,
            'number_voting_for': number_voting_for,
            'number_elected': number_elected,
            'contest_level0': contest_level0,
            'contest_level1': contest_level1,
            'contest_level2': contest_level2,
            'primary_party': primary_party,
            'district_scope': district_scope,
            'electorate_specifications': electorate_specifications,
            'special': special,
        }
        contest_office_manager = ContestOfficeManager()
        update_or_create_contest_office_results = contest_office_manager.update_or_create_contest_office(
            we_vote_id, google_civic_election_id, district_id, district_name, office_name, state_code,
            updated_contest_office_values)
    else:
        update_or_create_contest_office_results = {
            'success': False,
            'saved': 0,
            'updated': 0,
            'not_processed': 1,
        }

    if update_or_create_contest_office_results['success']:
        contest_office = update_or_create_contest_office_results['contest_office']
        contest_office_id = contest_office.id
        contest_office_we_vote_id = contest_office.we_vote_id
        ballot_item_display_name = contest_office.office_name
    else:
        contest_office_id = 0
        contest_office_we_vote_id = ''
        ballot_item_display_name = ''

    # If a voter_id was passed in, save an entry for this office for the voter's ballot
    if positive_value_exists(voter_id) and positive_value_exists(google_civic_election_id) \
            and positive_value_exists(contest_office_id):
        ballot_item_manager = BallotItemManager()
        ballot_item_manager.update_or_create_ballot_item_for_voter(
            voter_id, google_civic_election_id, google_ballot_placement, ballot_item_display_name,
            local_ballot_order, contest_office_id, contest_office_we_vote_id)
        # We leave off these and rely on default empty values: contest_measure_id, contest_measure_we_vote_id

    # Note: We do not need to connect the candidates with the voter here for a ballot item
    process_candidates_from_structured_json(
        candidates_structured_json, google_civic_election_id, ocd_division_id, state_code, contest_office_id,
        contest_office_we_vote_id)

    return update_or_create_contest_office_results
Ejemplo n.º 5
0
def process_contest_referendum_from_structured_json(
        one_contest_referendum_structured_json, google_civic_election_id,
        local_ballot_order, save_to_db):
    """
    "referendumTitle": "Proposition 45",
    "referendumSubtitle": "Healthcare Insurance. Rate Changes. Initiative Statute.",
    "referendumUrl": "http://vig.cdn.sos.ca.gov/2014/general/en/pdf/proposition-45-title-summary-analysis.pdf",
    "district" <= this is an array
    """
    logger.debug("Referendum contest_type")
    referendum_title = one_contest_referendum_structured_json[
        'referendumTitle']
    referendum_subtitle = one_contest_referendum_structured_json[
        'referendumSubtitle']
    referendum_url = one_contest_referendum_structured_json['referendumUrl']

    # These following fields exist for both candidates and referendum
    results = process_contest_common_fields_from_structured_json(
        one_contest_referendum_structured_json)
    ballot_placement = results[
        'ballot_placement']  # A number specifying the position of this contest
    # on the voter's ballot.
    primary_party = results[
        'primary_party']  # If this is a partisan election, the name of the party it is for.
    district_name = results['district_name']  # The name of the district.
    district_scope = results[
        'district_scope']  # The geographic scope of this district. If unspecified the
    # district's geography is not known. One of: national, statewide, congressional, stateUpper, stateLower,
    # countywide, judicial, schoolBoard, cityWide, township, countyCouncil, cityCouncil, ward, special
    district_ocd_id = results['district_ocd_id']
    electorate_specifications = results[
        'electorate_specifications']  # A description of any additional
    #  eligibility requirements for voting in this contest.
    special = results[
        'special']  # "Yes" or "No" depending on whether this a contest being held
    # outside the normal election cycle.

    if save_to_db:
        if referendum_title and referendum_subtitle and district_name and district_scope and district_ocd_id \
                and google_civic_election_id:
            try:
                query1 = GoogleCivicContestReferendum.objects.all()
                query1 = query1.filter(
                    referendum_title__exact=referendum_title)
                query1 = query1.filter(
                    google_civic_election_id__exact=google_civic_election_id)
                query1 = query1.filter(district_scope__exact=district_scope)

                # Was at least one existing entry found based on the above criteria?
                if len(query1):
                    google_civic_contest_referendum = query1[0]
                # If no entries found previously, create a new entry
                else:
                    google_civic_contest_referendum = \
                        GoogleCivicContestReferendum.objects.create(referendum_title=referendum_title,
                                                                    referendum_subtitle=referendum_subtitle,
                                                                    google_civic_election_id=google_civic_election_id,
                                                                    referendum_url=referendum_url,
                                                                    ballot_placement=ballot_placement,
                                                                    primary_party=primary_party,
                                                                    district_name=district_name,
                                                                    district_scope=district_scope,
                                                                    district_ocd_id=district_ocd_id,
                                                                    electorate_specifications=electorate_specifications,
                                                                    special=special,
                                                                    )

                # Save information about this contest item on the voter's ballot from: ballot_placement
            except Exception as e:
                handle_record_not_found_exception(e, logger=logger)

    return
Ejemplo n.º 6
0
def process_contest_office_from_structured_json(
        one_contest_office_structured_json, google_civic_election_id,
        local_ballot_order, save_to_db):
    voter_id = 1  # TODO Temp

    logger.debug("General contest_type")
    office = one_contest_office_structured_json['office']

    # The number of candidates that a voter may vote for in this contest.
    if 'numberVotingFor' in one_contest_office_structured_json:
        number_voting_for = one_contest_office_structured_json[
            'numberVotingFor']
    else:
        number_voting_for = 1

    # The number of candidates that will be elected to office in this contest.
    if 'numberElected' in one_contest_office_structured_json:
        number_elected = one_contest_office_structured_json['numberElected']
    else:
        number_elected = 1

    results = process_contest_common_fields_from_structured_json(
        one_contest_office_structured_json)
    ballot_placement = results[
        'ballot_placement']  # A number specifying the position of this contest
    # on the voter's ballot.
    primary_party = results[
        'primary_party']  # If this is a partisan election, the name of the party it is for.
    district_name = results['district_name']  # The name of the district.
    district_scope = results[
        'district_scope']  # The geographic scope of this district. If unspecified the
    # district's geography is not known. One of: national, statewide, congressional, stateUpper, stateLower,
    # countywide, judicial, schoolBoard, cityWide, township, countyCouncil, cityCouncil, ward, special
    district_ocd_id = results['district_ocd_id']
    electorate_specifications = results[
        'electorate_specifications']  # A description of any additional
    # eligibility requirements for voting in this contest.
    special = results[
        'special']  # "Yes" or "No" depending on whether this a contest being held
    # outside the normal election cycle.

    # We want to convert this from an array to three fields for the same table
    # levels: string, A list of office levels to filter by. Only offices that serve at least one of these levels
    # will be returned. Divisions that don't contain a matching office will not be returned. (repeated)
    # Allowed values
    #   administrativeArea1 -
    #   administrativeArea2 -
    #   country -
    #   international -
    #   locality -
    #   regional -
    #   special -
    #   subLocality1 -
    #   subLocality2 -
    # The levels of government of the office for this contest. There may be more than one in cases where a
    # jurisdiction effectively acts at two different levels of government; for example, the mayor of the
    # District of Columbia acts at "locality" level, but also effectively at both "administrative-area-2"
    # and "administrative-area-1".
    level_structured_json = one_contest_office_structured_json['level']
    contest_level = []
    for one_level in level_structured_json:
        contest_level.append(one_level)
    if 0 in contest_level:
        contest_level0 = contest_level[0]
    else:
        contest_level0 = ''
    if 1 in contest_level:
        contest_level1 = contest_level[1]
    else:
        contest_level1 = ''
    if 2 in contest_level:
        contest_level2 = contest_level[2]
    else:
        contest_level2 = ''

    # roles: string, A list of office roles to filter by. Only offices fulfilling one of these roles will be returned.
    # Divisions that don't contain a matching office will not be returned. (repeated)
    # Allowed values
    #   deputyHeadOfGovernment -
    #   executiveCouncil -
    #   governmentOfficer -
    #   headOfGovernment -
    #   headOfState -
    #   highestCourtJudge -
    #   judge -
    #   legislatorLowerBody -
    #   legislatorUpperBody -
    #   schoolBoard -
    #   specialPurposeOfficer -
    roles_structured_json = one_contest_office_structured_json['roles']
    # for one_role in roles_structured_json:
    # Figure out how we are going to use level info

    candidates = one_contest_office_structured_json['candidates']

    internal_contest_office_id = 0  # Set to 0 in case a new one is not created
    # Note that all of the information saved here is independent of a particular voter
    if save_to_db:
        if office and district_name and district_scope and district_ocd_id and google_civic_election_id:
            try:
                # Try to find earlier version based on name of the office google_civic_election_id
                query1 = GoogleCivicContestOffice.objects.all()
                query1 = query1.filter(
                    google_civic_election_id__exact=google_civic_election_id)
                query1 = query1.filter(district_scope__exact=district_scope)
                query1 = query1.filter(office__exact=office)

                # Was at least one existing entry found based on the above criteria?
                if len(query1):
                    google_civic_contest_office = query1[0]
                # If no entries found previously, create a new entry
                else:
                    google_civic_contest_office = \
                        GoogleCivicContestOffice.objects.create(office=office,
                                                                google_civic_election_id=google_civic_election_id,
                                                                number_voting_for=number_voting_for,
                                                                number_elected=number_elected,
                                                                contest_level0=contest_level0,
                                                                contest_level1=contest_level1,
                                                                contest_level2=contest_level2,
                                                                ballot_placement=ballot_placement,
                                                                primary_party=primary_party,
                                                                district_name=district_name,
                                                                district_scope=district_scope,
                                                                district_ocd_id=district_ocd_id,
                                                                electorate_specifications=electorate_specifications,
                                                                special=special,
                                                                )
                # The internal id is needed since there isn't a ContestOffice google identifier
                internal_contest_office_id = google_civic_contest_office.id
            except Exception as e:
                handle_record_not_found_exception(e, logger=logger)

        if value_exists(voter_id) and value_exists(
                google_civic_election_id) and value_exists(district_ocd_id):
            google_civic_ballot_item_manager = GoogleCivicBallotItemManager()
            google_civic_ballot_item_manager.save_ballot_item_for_voter(
                voter_id, google_civic_election_id, district_ocd_id,
                ballot_placement, local_ballot_order)

    process_candidates_from_structured_json(candidates,
                                            google_civic_election_id,
                                            internal_contest_office_id,
                                            save_to_db)

    return