Technical notes about Blueprint Kansas Inc. dba KSVotes.org
GitHub gives many technical details about the "non-profit-in-the-middle," the Kansas online voter registration site between voters and election offices
Updated Oct. 4, 2023 to show ksvotes.org/stats is no longer functioning.
This is the third of a three-part series on Blueprint Kansas Inc. doing business as KSVotes.org. This article looks at technical details from the files found on the Blueprint Kansas GitHub page. A computer programming background is helpful to understand this article.
Part 1: Blueprint Kansas Inc. doing business as KSVotes.org to register voters
There is a progressive “nonprofit in the middle” between a growing number of voters and election offices in KansasPart 2: History, Financials, Statistics of Blueprint Kansas Inc. dba KSVotes.org
Loud Light's Davis Hammet and Democratic Secretary of State Candidate Brian McClendon started KSVotes.org
Remove the “non-profit-in-the-middle” and avoid KSVotes.org by using an Official State of Kansas government website to register to vote. Look for "An official State of Kansas government website” when you register to vote.
Blueprint Kansas on GitHub
Please share what you find in studying the Blueprint Kansas repositories!
Overview of Repositories
A number of KSVotes.org-related files can be found in five repositories on the Blueprint Kansas GitHub page. Blueprint Kansas should be commended for offering this transparency to the public.
Knowledge of Python and Django is helpful in understanding these repositories and files.
Click on a repository name to see a list of files and brief descriptions. The files in a repository can be downloaded in a .zip by clicking on the green Code button.
Two main repositories have files of most interest:
ksvotes.org is the main repository for the current “version 2” of the online website.
v3.ksvotes.org is a “version 3” that is under development using Django.
Three other repositories address specific needs:
form-filler.py provides Imagemagick image manipulation to create forms that contain images, such as a signature, along with text. ksvotes.org allows a voter to enter a signature that must be included with a voter registration form or an advance ballot request form emailed to a county clerk.
ksmyvoteinfo-py provides Python code to “screen scrape” the Kansas Secretary of State’s VoterView site to verify registration information about a voter.
NVRIS provides a National Voting Registration Imaging Service, which was adapted from one developed by Jake Lowen at GPS Impact.
Repository Notes
I focused on v3.kansasvoes.org, but I think ksvotes.org may be the current system.
ksvotes.org
Contributors to KSvotes.org include:
.env-dist: This is curious.
# Don't use this secret key in production obviously
SECRET_KEY="A5Wvv2bXvN39PqXkDYvscZermVVzMnCbw6m4XRoIXDEdCHzHqK"
county-clerks.csv: Names and addresses of Kansas county clerks and election officials with email addresses for where to send registration and advance ballot forms. How is this file maintained? How often is it updated? Osage County has had several county clerks this year.
app/main/VR/example_form.py
This file has Python code to create a sample signature_img_string for the signature needed for the voter registration or advance ballot form:
The signature_img_string above can be displayed in an HTML file as a 396-by-56 pixel image. The checkered background here means it is transparent.
At this time it’s unclear what the actual image size is for the signature captured by ksvotes.org.
It’s also unclear how long signature files are retained or what else they might be used for.
app/templates: Variety of parameterized HTML templates
bin/abatch.py: Creates report and file about advanced ballots (AB) matching requests to KSVotes.org versus advance ballots requested through county election offices. The match was against Secretary of State sent/received/early in-person (EIP) voting data.
Example output from July 22, 2020:
# AB_NOTREQUESTED 120411
# ABMATCH_SENT 15386
# AB_NOTREQUESTED_VIA_KSVOTES_BUT_SENT 11663
# AB_REQUESTED_FOR_ANOTHER_2020_ELECTION 2895
# AB_REQUESTED 2724
# ABMATCH_SENT_KSV_PERMANENT 867
# AB_KSV_PERMANENT_REQ 342
# AB_KSV_PERMANENT_NONREQ 23
# AB_NOTREQUESTED_VIA_KSVOTES_BUT_SENT_AND_RETURNED 4
# AB_EIP 2
v3.ksvotes.org - Django port of ksvotes.org
config/settings.py: EMAIL_BCC environment variable suggests all emails sent by system are BCC’d to registration@ksvotes.org.
EMAIL_BCC = env("EMAIL_BCC", "registration@ksvotes.org")
ksvotes/services/usps_api.py: Code suggests address checks are made against US Post Office API to validate address, but addresses seemed to have been processed regardless of validity.
ksvotes/services/registrant_exporter.py,
ksvotes/management/commands/export_registrants.py
Shows what fields are exported for registrants, and which ones are skipped, e.g., identification numbers and the signature_string.
def export(self):
skip_fields = [
"csrf_token",
"identification",
"ab_identification",
"vr_form",
"ab_forms",
"signature_string",
]
fieldnames = [
"id",
"created_at",
"updated_at",
"vr_completed_at",
"ab_completed_at",
"redacted_at",
"ab_permanent",
"session_id",
"ref",
"is_citizen",
"is_eighteen",
"dob_year",
"party",
"county",
"lang",
"signed_at",
"reg_lookup_complete",
"addr_lookup_complete",
"reg_found",
"identification_found",
"ab_identification_found",
"user_agent",
"r_ref",
"r_name_first",
"r_name_last",
"r_dob",
"r_county",
"r_email",
"r_phone",
"r_sos_reg",
"r_skip_sos",
"r_prefix",
"r_name_middle",
"r_suffix",
"r_has_prev_name",
"r_prev_prefix",
"r_prev_name_first",
"r_prev_name_middle",
"r_prev_name_last",
"r_prev_suffix",
"r_addr",
"r_unit",
"r_city",
"r_state",
"r_zip",
"r_has_prev_addr",
"r_prev_addr",
"r_prev_unit",
"r_prev_city",
"r_prev_state",
"r_prev_zip",
"r_has_mail_addr",
"r_mail_addr",
"r_mail_unit",
"r_mail_city",
"r_mail_state",
"r_mail_zip",
"r_validated_addresses",
"r_affirmation",
"r_vr_form_message_id",
"r_recaptcha",
"r_elections",
"r_sos_failure",
"r_perm_reason",
"r_ab_forms_message_id",
"r_ab_id_action_email_sent",
]
ksvotes/urls.py
urlpatterns in Django defines valid URLs that are processed, e.g., ksvotes.org/stats/
path("stats/", home.stats, name="home.stats"),
ksvotes.org/stats is an up-to-date display of recent activity of the site from Aug. 26, 2023:
Too much sunlight for Blueprint Kansas on this stats web page? By Oct. 2023, KSVotes.org dropped this stats page. (Or is it now hidden somewhere else?)
form-filler.py
ksmyvoteinfo-py
Uses BeautifulSoup to screen-scrape https://myvoteinfo.voteks.org/voterview to lookup registration information for specific voter using fetch_registrant function
Information is displayed via app/templates/voter-view-details.html
NVRIS
src/routes/FLEX.js: // render signature to file
General Comments
Protection of personal information.
The name Kris Kobach and his date of birth were used in eight test scripts across the v3.ksvotes.org and ksvotes.org GitHub repositories. Kobach’s info was used to test screen scraping of Secretary of State Voter View site, likely because Kobach was the Secretary of State when ksvotes.org was developed.
Blueprint Kansas’ Project Manager Peter Karman’s name and email address appeared in two files. Karman’s street address in Lawrence appeared in two other files as a “legit address.”