From 6207e42ebd14411e707a025e4a6bf9d18baf671d Mon Sep 17 00:00:00 2001 From: Dale John McGrew Date: Sat, 19 Sep 2026 13:07:20 -0700 Subject: [PATCH 1/2] Improved automatic deduping of candidates (Iteration 2) --- candidate/controllers.py | 34 +++++-- templates/candidate/candidate_merge.html | 89 +++++++++++++++++-- .../candidate_merge_one_field_decision.html | 8 ++ wevote_functions/functions.py | 14 ++- 4 files changed, 130 insertions(+), 15 deletions(-) diff --git a/candidate/controllers.py b/candidate/controllers.py index 15587ed7d..241e6bbb4 100644 --- a/candidate/controllers.py +++ b/candidate/controllers.py @@ -30,7 +30,7 @@ from wevote_functions.functions import add_period_to_middle_name_initial, add_period_to_name_prefix_and_suffix, \ convert_to_int, convert_to_political_party_constant, \ extract_instagram_handle_from_text_string, extract_twitter_handle_from_text_string, extract_website_from_url, \ - is_url_valid, MIDDLE_INITIAL_SUBSTRINGS, \ + is_url_valid, MIDDLE_INITIAL_WITH_PERIOD_SUBSTRINGS, MIDDLE_INITIAL_WITHOUT_PERIOD_SUBSTRINGS, \ normalize_sms_phone_number_for_voter_update, positive_value_exists, process_request_from_master, \ remove_period_from_middle_name_initial, remove_period_from_name_prefix_and_suffix from wevote_functions.functions_date import convert_date_to_we_vote_date_string, \ @@ -393,9 +393,9 @@ def choose_best_candidate_url( best_url_string = 'CANDIDATE1' results = { - 'best_url': best_url_string, - 'status': status, - 'success': success, + 'best_url_string': best_url_string, + 'status': status, + 'success': success, } return results @@ -631,16 +631,31 @@ def preferred_name_identifier(candidate1_name, candidate2_name): # For example, Kristi C. Morris <=> Kristi Morris: Should choose "Kristi Morris" candidate1_has_middle_initial = False candidate2_has_middle_initial = False + + # Did we find an exact match in the name for a middle initial WITH a period at the end? modified_candidate1_name = candidate1_name modified_candidate2_name = candidate2_name - for middle_initial_substring in MIDDLE_INITIAL_SUBSTRINGS: + for middle_initial_substring in MIDDLE_INITIAL_WITH_PERIOD_SUBSTRINGS: # Remove middle_initial_substring modified_candidate1_name = modified_candidate1_name.replace(middle_initial_substring, " ") modified_candidate2_name = modified_candidate2_name.replace(middle_initial_substring, " ") + # Check if the difference between these two lengths is 4 characters, independent of whichever one is longer + if abs(len(candidate1_name) - len(modified_candidate1_name)) == 4: + candidate1_has_middle_initial = True + if abs(len(candidate2_name) - len(modified_candidate2_name)) == 4: + candidate2_has_middle_initial = True - if len(candidate1_name) != len(modified_candidate1_name): + # Did we find an exact match in the name for a middle initial WITHOUT a period at the end? + modified_candidate1_name = candidate1_name + modified_candidate2_name = candidate2_name + for middle_initial_substring in MIDDLE_INITIAL_WITHOUT_PERIOD_SUBSTRINGS: + # Remove middle_initial_substring + modified_candidate1_name = modified_candidate1_name.replace(middle_initial_substring, " ") + modified_candidate2_name = modified_candidate2_name.replace(middle_initial_substring, " ") + # Check if the difference between these two lengths is 3 characters, independent of whichever one is longer + if abs(len(candidate1_name) - len(modified_candidate1_name)) == 3: candidate1_has_middle_initial = True - if len(candidate2_name) != len(modified_candidate2_name): + if abs(len(candidate2_name) - len(modified_candidate2_name)) == 3: candidate2_has_middle_initial = True if candidate1_has_middle_initial and candidate2_has_middle_initial: @@ -661,6 +676,10 @@ def merge_if_duplicate_candidates(candidate1_on_stage, candidate2_on_stage, conf candidate1_we_vote_id = candidate1_on_stage.we_vote_id candidate2_we_vote_id = candidate2_on_stage.we_vote_id + # look_at_list = ['wv2mcand9540', 'wv2mcand26630'] + # if candidate1_we_vote_id in look_at_list or candidate2_we_vote_id in look_at_list: + # status += "CANDIDATE_WE_WANT_TO_LOOK_AT " + # conflict_values mostly comes from figure_out_candidate_conflict_values # Are there any comparisons that require admin intervention? @@ -669,6 +688,7 @@ def merge_if_duplicate_candidates(candidate1_on_stage, candidate2_on_stage, conf # Don't let conflict stop us with any of these fields if attribute == "ballotpedia_candidate_id" \ or attribute == "ballotpedia_office_id" \ + or attribute == "ballotpedia_photo_url" \ or attribute == "ballotpedia_race_id" \ or attribute == "candidate_url" \ or attribute == "facebook_url" \ diff --git a/templates/candidate/candidate_merge.html b/templates/candidate/candidate_merge.html index 0c29da0e6..c273291a8 100644 --- a/templates/candidate/candidate_merge.html +++ b/templates/candidate/candidate_merge.html @@ -93,7 +93,7 @@

- {% include "candidate/candidate_merge_one_field_decision.html" with field_name="politician_we_vote_id" field_label="Politician We Vote ID" conflict_status=conflict_values.politician_we_vote_id candidate1_field_value=candidate_option1.politician_we_vote_id candidate2_field_value=candidate_option2.politician_we_vote_id candidate1=candidate_option1 candidate2=candidate_option2 %} + {% include "candidate/candidate_merge_one_field_decision.html" with field_name="politician_we_vote_id" field_label="Politician WeVote ID" conflict_status=conflict_values.politician_we_vote_id candidate1_field_value=candidate_option1.politician_we_vote_id candidate2_field_value=candidate_option2.politician_we_vote_id candidate1=candidate_option1 candidate2=candidate_option2 %} {% include "candidate/candidate_merge_one_field_decision.html" with field_name="politician_id" field_label="Politician ID" conflict_status=conflict_values.politician_id candidate1_field_value=candidate_option1.politician_id candidate2_field_value=candidate_option2.politician_id candidate1=candidate_option1 candidate2=candidate_option2 %} @@ -168,7 +168,7 @@

- {#{% include "candidate/candidate_merge_one_field_decision.html" with field_name="contest_office_we_vote_id" field_label="Office We Vote Id" conflict_status=conflict_values.contest_office_we_vote_id candidate1_field_value=candidate_option1.contest_office_we_vote_id candidate2_field_value=candidate_option2.contest_office_we_vote_id candidate1=candidate_option1 candidate2=candidate_option2 %}#} + {#{% include "candidate/candidate_merge_one_field_decision.html" with field_name="contest_office_we_vote_id" field_label="Office WeVote Id" conflict_status=conflict_values.contest_office_we_vote_id candidate1_field_value=candidate_option1.contest_office_we_vote_id candidate2_field_value=candidate_option2.contest_office_we_vote_id candidate1=candidate_option1 candidate2=candidate_option2 %}#} {% include "candidate/candidate_merge_one_field_decision.html" with field_name="contest_office_name" field_label="Office Name" conflict_status=conflict_values.contest_office_name candidate1_field_value=candidate_option1.contest_office_name candidate2_field_value=candidate_option2.contest_office_name candidate1=candidate_option1 candidate2=candidate_option2 %} @@ -189,7 +189,7 @@

{% endif %} {% if candidate_option1.we_vote_id != None or candidate_option2.we_vote_id != None %} - We Vote ID + WeVote ID {{ candidate_option1.we_vote_id|default_if_none:"" }} {{ candidate_option2.we_vote_id|default_if_none:"" }} @@ -329,12 +329,32 @@

{% include "candidate/candidate_merge_one_field_decision.html" with field_name="profile_image_type_currently_active" field_label="Image Currently Active" conflict_status=conflict_values.profile_image_type_currently_active candidate1_field_value=candidate_option1.profile_image_type_currently_active candidate2_field_value=candidate_option2.profile_image_type_currently_active candidate1=candidate_option1 candidate2=candidate_option2 %} + {% if candidate_option1.we_vote_hosted_profile_image_url_medium or candidate_option2.we_vote_hosted_profile_image_url_medium %} + + WeVote Image + + {% if candidate_option1.we_vote_hosted_profile_image_url_medium %} + + {% endif %} + + + {% if candidate_option2.we_vote_hosted_profile_image_url_medium %} + + {% endif %} + + + {% endif %} + {# Displayed photo #} - {% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_image_url_tiny" field_label="We Vote Image (Tiny)" conflict_status=conflict_values.we_vote_hosted_profile_image_url_tiny candidate1_field_value=candidate_option1.we_vote_hosted_profile_image_url_tiny candidate2_field_value=candidate_option2.we_vote_hosted_profile_image_url_tiny candidate1=candidate_option1 candidate2=candidate_option2 %} + {% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_image_url_tiny" field_label="WeVote Image (Tiny)" conflict_status=conflict_values.we_vote_hosted_profile_image_url_tiny candidate1_field_value=candidate_option1.we_vote_hosted_profile_image_url_tiny candidate2_field_value=candidate_option2.we_vote_hosted_profile_image_url_tiny candidate1=candidate_option1 candidate2=candidate_option2 %} - {% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_image_url_medium" field_label="We Vote Image (Medium)" conflict_status=conflict_values.we_vote_hosted_profile_image_url_medium candidate1_field_value=candidate_option1.we_vote_hosted_profile_image_url_medium candidate2_field_value=candidate_option2.we_vote_hosted_profile_image_url_medium candidate1=candidate_option1 candidate2=candidate_option2 %} + {% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_image_url_medium" field_label="WeVote Image (Medium)" conflict_status=conflict_values.we_vote_hosted_profile_image_url_medium candidate1_field_value=candidate_option1.we_vote_hosted_profile_image_url_medium candidate2_field_value=candidate_option2.we_vote_hosted_profile_image_url_medium candidate1=candidate_option1 candidate2=candidate_option2 %} - {% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_image_url_large" field_label="We Vote Image (Large)" conflict_status=conflict_values.we_vote_hosted_profile_image_url_large candidate1_field_value=candidate_option1.we_vote_hosted_profile_image_url_large candidate2_field_value=candidate_option2.we_vote_hosted_profile_image_url_large candidate1=candidate_option1 candidate2=candidate_option2 %} + {% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_image_url_large" field_label="WeVote Image (Large)" conflict_status=conflict_values.we_vote_hosted_profile_image_url_large candidate1_field_value=candidate_option1.we_vote_hosted_profile_image_url_large candidate2_field_value=candidate_option2.we_vote_hosted_profile_image_url_large candidate1=candidate_option1 candidate2=candidate_option2 %} {# Ballotpedia photo #} {% include "candidate/candidate_merge_one_field_decision.html" with field_name="ballotpedia_profile_image_url_https" field_label="Ballotpedia Image" conflict_status=conflict_values.ballotpedia_profile_image_url_https candidate1_field_value=candidate_option1.ballotpedia_profile_image_url_https candidate2_field_value=candidate_option2.ballotpedia_profile_image_url_https candidate1=candidate_option1 candidate2=candidate_option2 %} @@ -347,6 +367,25 @@

{% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_facebook_image_url_large" field_label="Facebook Image (Large)" conflict_status=conflict_values.we_vote_hosted_profile_facebook_image_url_large candidate1_field_value=candidate_option1.we_vote_hosted_profile_facebook_image_url_large candidate2_field_value=candidate_option2.we_vote_hosted_profile_facebook_image_url_large candidate1=candidate_option1 candidate2=candidate_option2 %} {# Twitter photo #} + {% if candidate_option1.we_vote_hosted_profile_twitter_image_url_large or candidate_option2.we_vote_hosted_profile_twitter_image_url_large %} + + Twitter Image + + {% if candidate_option1.we_vote_hosted_profile_twitter_image_url_large %} + + {% endif %} + + + {% if candidate_option2.we_vote_hosted_profile_twitter_image_url_large %} + + {% endif %} + + + {% endif %} {% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_twitter_image_url_tiny" field_label="Twitter Image (Tiny)" conflict_status=conflict_values.we_vote_hosted_profile_twitter_image_url_tiny candidate1_field_value=candidate_option1.we_vote_hosted_profile_twitter_image_url_tiny candidate2_field_value=candidate_option2.we_vote_hosted_profile_twitter_image_url_tiny candidate1=candidate_option1 candidate2=candidate_option2 %} {% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_twitter_image_url_medium" field_label="Twitter Image (Medium)" conflict_status=conflict_values.we_vote_hosted_profile_twitter_image_url_medium candidate1_field_value=candidate_option1.we_vote_hosted_profile_twitter_image_url_medium candidate2_field_value=candidate_option2.we_vote_hosted_profile_twitter_image_url_medium candidate1=candidate_option1 candidate2=candidate_option2 %} @@ -354,6 +393,25 @@

{% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_twitter_image_url_large" field_label="Twitter Image (Large)" conflict_status=conflict_values.we_vote_hosted_profile_twitter_image_url_large candidate1_field_value=candidate_option1.we_vote_hosted_profile_twitter_image_url_large candidate2_field_value=candidate_option2.we_vote_hosted_profile_twitter_image_url_large candidate1=candidate_option1 candidate2=candidate_option2 %} {# Uploaded photo #} + {% if candidate_option1.we_vote_hosted_profile_uploaded_image_url_large or candidate_option2.we_vote_hosted_profile_uploaded_image_url_large %} + + Uploaded Image + + {% if candidate_option1.we_vote_hosted_profile_uploaded_image_url_large %} + + {% endif %} + + + {% if candidate_option2.we_vote_hosted_profile_uploaded_image_url_large %} + + {% endif %} + + + {% endif %} {% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_uploaded_image_url_tiny" field_label="Uploaded Image (Tiny)" conflict_status=conflict_values.we_vote_hosted_profile_uploaded_image_url_tiny candidate1_field_value=candidate_option1.we_vote_hosted_profile_uploaded_image_url_tiny candidate2_field_value=candidate_option2.we_vote_hosted_profile_uploaded_image_url_tiny candidate1=candidate_option1 candidate2=candidate_option2 %} {% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_uploaded_image_url_medium" field_label="Uploaded Image (Medium)" conflict_status=conflict_values.we_vote_hosted_profile_uploaded_image_url_medium candidate1_field_value=candidate_option1.we_vote_hosted_profile_uploaded_image_url_medium candidate2_field_value=candidate_option2.we_vote_hosted_profile_uploaded_image_url_medium candidate1=candidate_option1 candidate2=candidate_option2 %} @@ -361,6 +419,25 @@

{% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_uploaded_image_url_large" field_label="Uploaded Image (Large)" conflict_status=conflict_values.we_vote_hosted_profile_uploaded_image_url_large candidate1_field_value=candidate_option1.we_vote_hosted_profile_uploaded_image_url_large candidate2_field_value=candidate_option2.we_vote_hosted_profile_uploaded_image_url_large candidate1=candidate_option1 candidate2=candidate_option2 %} {# Vote USA photo #} + {% if candidate_option1.we_vote_hosted_profile_vote_usa_image_url_large or candidate_option2.we_vote_hosted_profile_vote_usa_image_url_large %} + + Vote USA Image + + {% if candidate_option1.we_vote_hosted_profile_vote_usa_image_url_large %} + + {% endif %} + + + {% if candidate_option2.we_vote_hosted_profile_vote_usa_image_url_large %} + + {% endif %} + + + {% endif %} {% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_vote_usa_image_url_tiny" field_label="Vote USA Image (Tiny)" conflict_status=conflict_values.we_vote_hosted_profile_vote_usa_image_url_tiny candidate1_field_value=candidate_option1.we_vote_hosted_profile_vote_usa_image_url_tiny candidate2_field_value=candidate_option2.we_vote_hosted_profile_vote_usa_image_url_tiny candidate1=candidate_option1 candidate2=candidate_option2 %} {% include "candidate/candidate_merge_one_field_decision.html" with field_name="we_vote_hosted_profile_vote_usa_image_url_medium" field_label="Vote USA Image (Medium)" conflict_status=conflict_values.we_vote_hosted_profile_vote_usa_image_url_medium candidate1_field_value=candidate_option1.we_vote_hosted_profile_vote_usa_image_url_medium candidate2_field_value=candidate_option2.we_vote_hosted_profile_vote_usa_image_url_medium candidate1=candidate_option1 candidate2=candidate_option2 %} diff --git a/templates/candidate/candidate_merge_one_field_decision.html b/templates/candidate/candidate_merge_one_field_decision.html index 6c4394a9f..304564569 100644 --- a/templates/candidate/candidate_merge_one_field_decision.html +++ b/templates/candidate/candidate_merge_one_field_decision.html @@ -14,6 +14,10 @@   + {% elif 'www' in candidate1_field_value or '.com' in candidate1_field_value %} + + +   {% endif %} @@ -25,6 +29,10 @@ + {% elif 'www' in candidate2_field_value or '.com' in candidate2_field_value %} + + + {% endif %} {% else %} diff --git a/wevote_functions/functions.py b/wevote_functions/functions.py index 0b6b37cd6..db88a2bd2 100644 --- a/wevote_functions/functions.py +++ b/wevote_functions/functions.py @@ -455,13 +455,23 @@ ['{district_number}th congressional district', 'district {district_number}'], ] -MIDDLE_INITIAL_SUBSTRINGS = [] LETTER_LIST = string.ascii_uppercase +MIDDLE_INITIAL_SUBSTRINGS = [] for letter in LETTER_LIST: + letter_substring = " {letter}. ".format(letter=letter) + MIDDLE_INITIAL_SUBSTRINGS.append(letter_substring) letter_substring = " {letter} ".format(letter=letter) MIDDLE_INITIAL_SUBSTRINGS.append(letter_substring) + +MIDDLE_INITIAL_WITH_PERIOD_SUBSTRINGS = [] +for letter in LETTER_LIST: letter_substring = " {letter}. ".format(letter=letter) - MIDDLE_INITIAL_SUBSTRINGS.append(letter_substring) + MIDDLE_INITIAL_WITH_PERIOD_SUBSTRINGS.append(letter_substring) + +MIDDLE_INITIAL_WITHOUT_PERIOD_SUBSTRINGS = [] +for letter in LETTER_LIST: + letter_substring = " {letter} ".format(letter=letter) + MIDDLE_INITIAL_WITHOUT_PERIOD_SUBSTRINGS.append(letter_substring) def add_to_list_if_positive_value_exists(value=None, incoming_list=[]): From 6b73b34b7131f55a50f7d4aa6f598253bc69bdd5 Mon Sep 17 00:00:00 2001 From: Dale John McGrew Date: Mon, 21 Sep 2026 17:55:52 -0700 Subject: [PATCH 2/2] Added manual way to search for Possible Duplicate Politicians. --- politician/views_admin.py | 78 ++++++++++++++++++++++- templates/candidate/candidate_edit.html | 2 +- templates/politician/politician_edit.html | 45 +++++++++---- 3 files changed, 108 insertions(+), 17 deletions(-) diff --git a/politician/views_admin.py b/politician/views_admin.py index acad90950..96bc75525 100644 --- a/politician/views_admin.py +++ b/politician/views_admin.py @@ -1447,6 +1447,7 @@ def politician_edit_view(request, politician_id=0, politician_we_vote_id=''): ballotpedia_politician_url = request.GET.get('ballotpedia_politician_url', False) birth_date = request.GET.get('birth_date', False) ballotpedia_politician_name = request.GET.get('ballotpedia_politician_name', False) + politician_search = request.GET.get('politician_search', False) facebook_url = request.GET.get('facebook_url', False) facebook_url2 = request.GET.get('facebook_url2', False) facebook_url3 = request.GET.get('facebook_url3', False) @@ -1728,7 +1729,79 @@ def politician_edit_view(request, politician_id=0, politician_we_vote_id=''): description="Retrieve Politician objects for this politician") duplicate_politician_list = [] - if positive_value_exists(politician_on_stage.politician_name) or \ + if positive_value_exists(politician_search): + try: + duplicate_politician_list = Politician.objects.using('readonly').all() + duplicate_politician_list = duplicate_politician_list.exclude( + we_vote_id=politician_on_stage.we_vote_id) + duplicate_politician_list = duplicate_politician_list.filter( + state_code__iexact=politician_on_stage.state_code) + + search_words = politician_search.split() + for one_word in search_words: + filters = [] + + new_filter = Q(first_name__iexact=one_word) + filters.append(new_filter) + + new_filter = ( + Q(google_civic_candidate_name__icontains=one_word) | + Q(google_civic_candidate_name2__icontains=one_word) | + Q(google_civic_candidate_name3__icontains=one_word) + ) + filters.append(new_filter) + + new_filter = Q(last_name__iexact=one_word) + filters.append(new_filter) + + new_filter = Q(linked_campaignx_we_vote_id=one_word) + filters.append(new_filter) + + new_filter = ( + Q(politician_email__icontains=one_word) | + Q(politician_email2__icontains=one_word) | + Q(politician_email3__icontains=one_word) + ) + filters.append(new_filter) + + new_filter = Q(politician_name__icontains=one_word) + filters.append(new_filter) + + new_filter = ( + Q(politician_twitter_handle__icontains=one_word) | + Q(politician_twitter_handle2__icontains=one_word) | + Q(politician_twitter_handle3__icontains=one_word) | + Q(politician_twitter_handle4__icontains=one_word) | + Q(politician_twitter_handle5__icontains=one_word) + ) + filters.append(new_filter) + + new_filter = Q(political_party__icontains=one_word) + filters.append(new_filter) + + new_filter = Q(seo_friendly_path__icontains=one_word) + filters.append(new_filter) + + new_filter = Q(vote_usa_politician_id__icontains=one_word) + filters.append(new_filter) + + new_filter = Q(we_vote_id=one_word) + filters.append(new_filter) + + # Add the first query + if len(filters): + final_filters = filters.pop() + + # ...and "OR" the remaining items in the list + for item in filters: + final_filters |= item + + duplicate_politician_list = duplicate_politician_list.filter(final_filters) + duplicate_politician_list = duplicate_politician_list.order_by('politician_name')[:20] + except ObjectDoesNotExist: + # This is fine, create new + pass + elif positive_value_exists(politician_on_stage.politician_name) or \ positive_value_exists(politician_on_stage.first_name) or \ positive_value_exists(politician_on_stage.last_name) or \ positive_value_exists(politician_on_stage.politician_twitter_handle) or \ @@ -1826,7 +1899,8 @@ def politician_edit_view(request, politician_id=0, politician_we_vote_id=''): except ObjectDoesNotExist: # This is fine, create new pass - speed_statistics.end(context="Find possible duplicate politicians") + + speed_statistics.end(context="Find possible duplicate politicians") # ################################## # Find Representatives Linked to this Politician diff --git a/templates/candidate/candidate_edit.html b/templates/candidate/candidate_edit.html index de669973e..c62a7bc73 100644 --- a/templates/candidate/candidate_edit.html +++ b/templates/candidate/candidate_edit.html @@ -1032,7 +1032,6 @@


- {% if related_candidate_list %}

Possible Duplicate Candidates for {{ candidate.candidate_name }}

+ {% if related_candidate_list %} diff --git a/templates/politician/politician_edit.html b/templates/politician/politician_edit.html index dc77e9ac9..23dc60aa0 100644 --- a/templates/politician/politician_edit.html +++ b/templates/politician/politician_edit.html @@ -1477,22 +1477,31 @@

Related Politicians

(no recommended politicians found)

{% endif %} -{% if duplicate_politician_list %} + +

Possible Duplicate Politicians

-
- - {% if use_trigram_match %} - ✓ Currently viewing fuzzy matching results - {% else %} - Currently viewing exact matching results - {% endif %} +
+
+ +
+ +
+
+ +{% if duplicate_politician_list %}
@@ -1581,8 +1590,16 @@

Possible Duplicate Politicians

{% endfor %}
 
+{% else %} +

 

+

 

+

No politicians found to merge with this politician.

+

 

+

 

{% endif %} + +