prep_candidate_listing <- function( path_candidate_listing_raw ) { candidate_listing_contest <- prep_open_dataset_db(fs::path_dir(path_candidate_listing_raw)) |> filter(name_on_ballot != "No Preference") |> tidyr::replace_na(list( first_name = "", middle_name = "", last_name = "", name_suffix_lbl = "" )) # candidate names ---- # Extract candidate names, these will be primary keys for the candidates table candidate_names <- candidate_listing_contest |> dbplyr::window_order(last_name, first_name, middle_name) |> distinct(first_name, middle_name, last_name, name_suffix_lbl) |> mutate(candidate_id = row_number(), .before = 1) # candidate_name_on_ballot ---- candidate_name_on_ballot <- extract_candidate_info( candidate_listing_contest, candidate_names, info_vars = c("name_on_ballot") ) |> collect() # candidate_address ---- candidate_address <- candidate_listing_contest |> mutate( phone = coalesce(phone, office_phone, business_phone), street_address = toupper(street_address), street_address = REGEXP_REPLACE(street_address, " +", " ", "g"), street_address = trimws(street_address), ) |> select(-office_phone, -business_phone) |> extract_candidate_info( candidate_names, info_vars = c("street_address", "city", "state", "zip_code", "phone", "email") ) |> collect() |> rename(street = street_address) # candidate_party ---- candidate_party <- candidate_listing_contest |> extract_candidate_info( candidate_names, info_vars = c("party_candidate") ) |> collect() |> mutate(party_candidate = forcats::fct_inorder(party_candidate)) # Extract contests (remaining data in candidate_listing) ---- cols_candidate_id <- intersect(colnames(candidate_names), colnames(candidate_listing_contest)) cols_related <- setdiff( c(colnames(candidate_name_on_ballot), colnames(candidate_address), colnames(candidate_party)), "election_dt" ) contests <- candidate_listing_contest |> select(election_dt:name_suffix_lbl) |> select(-any_of(cols_related)) |> left_join(candidate_names, by = cols_candidate_id) |> relocate(candidate_id, .before = first_name) |> collect() # Get current complete contact information ---- candidate_contact_current <- candidate_listing_current_contact_info(candidate_address) # Join candidates into one big table ---- candidates <- candidate_names |> collect() |> left_join( candidate_name_on_ballot |> slice_max(election_dt, by = candidate_id, n = 1) |> select(-election_dt), by = "candidate_id", relationship = "one-to-one" ) |> left_join( candidate_contact_current, by = "candidate_id", relationship = "one-to-one" ) |> left_join( candidate_party |> filter(!is.na(party_candidate)) |> group_by(candidate_id) |> slice_max(election_dt, n = 1) |> arrange(party_candidate) |> slice_head(n = 1) |> ungroup() |> select(-election_dt) |> rename(party_last = party_candidate), by = "candidate_id", relationship = "one-to-one" ) |> left_join( candidate_party |> filter(!is.na(party_candidate)) |> group_by(candidate_id) |> count(party_candidate) |> slice_max(n, n = 1) |> arrange(party_candidate) |> slice_head(n = 1) |> ungroup() |> select(-n) |> rename(party_most = party_candidate), by = "candidate_id", relationship = "one-to-one" ) |> left_join( contests |> group_by(candidate_id) |> distinct(candidate_id, election_dt, contest_name) |> summarize( contest_n = n(), contest_first = min(election_dt), contest_latest = max(election_dt) ), by = "candidate_id", relationship = "one-to-one" ) |> relocate(name_on_ballot, .before = first_name) |> relocate(starts_with("party"), .before = street) |> relocate(starts_with("contest"), .before = street) |> add_address_lookup(street = street, postal_code = zip_code) # Return list of tables list( elections = contests, candidates = candidates, candidate_name_on_ballot = candidate_name_on_ballot, candidate_contact = candidate_address, candidate_party = candidate_party ) } extract_candidate_info <- function( candidate_listing_contest, candidate_names, info_vars ) { candidate_listing_contest |> select(first_name:name_suffix_lbl, election_dt, all_of(info_vars)) |> distinct() |> left_join( candidate_names, by = c("first_name", "middle_name", "last_name", "name_suffix_lbl") ) |> select(-first_name, -middle_name, -last_name, -name_suffix_lbl) |> relocate(candidate_id, .before = 1) |> distinct() |> arrange(candidate_id, election_dt, !!info_vars) } candidate_listing_current_contact_info <- function(candidate_address) { candidate_phone_current <- candidate_address |> filter(!is.na(phone)) |> slice_max(election_dt, by = candidate_id, n = 1, with_ties = FALSE) |> select(candidate_id, phone) candidate_email_current <- candidate_address |> filter(!is.na(email)) |> slice_max(election_dt, by = candidate_id, n = 1, with_ties = FALSE) |> select(candidate_id, email) candidate_address_current <- candidate_address |> group_by(candidate_id) |> slice_max(election_dt, n = 1) |> select(-election_dt) |> filter( n_distinct(street) == 1 | !grepl("PO BOX", street) ) |> slice_head(n = 1) |> select(candidate_id, street:zip_code) |> ungroup() candidate_address_current |> left_join(candidate_phone_current, by = "candidate_id", relationship = "one-to-one") |> left_join(candidate_email_current, by = "candidate_id", relationship = "one-to-one") }