This project contains a small collection of Python scripts to generate HTML snippets of the round-by-round results of ranked choice voting (RCV) contests from the RCV results reports generated by Dominion's Democracy Suite voting system. The project is open source.
The project supports parsing both the XML and Excel (.xlsx
) RCV reports
from Dominion's system. (XML is the preferred report to parse
since it's more structured and is an open data format.)
For an interactive demo page showing examples generated for the 16 RCV contests held in San Francisco in the four elections between 2019 and 2022, go here:
Also see a memo about this project that was included in the agenda packet of the San Francisco Elections Commission's September 20, 2023 Regular Meeting under agenda item #5.
This project enables the round-by-round results of Dominion RCV contests to be displayed in an accessible, HTML format in multiple languages, in addition to Dominion's default, English-only formats of PDF, Excel, and XML.
For example, below is a screenshot taken from the demo above of the round-by-round page this project produces for San Francisco's November 2022 District Attorney contest.
(The HTML format above is an improved version of the English-only HTML format that Dominion's voting system used to generate before 2019 but doesn't anymore. See here for an example of that from a 2018 contest.)
Hovering over an RCV-related term in the table shows a definition, as in the screenshot below. This helps people understand the results and what they are looking at.
This project can also output RCV results in a short summary format for inclusion in a results summary page of several contests. For example, below is another screenshot from the demo of the summary this project made for the same contest as above.
The format of the summary table above is closely modeled after the Center for Civic Design's May 2023 best practice recommendations for summary displays. (See the "Results Displays in News Articles" PDF on the linked page, which the document emphasizes is also relevant to election office results displays).
Notice how the summary shows both the first and last rounds, and the leading candidate (and their final-round vote total) is highlighted in green. The final round is the most important round to show because it shows which candidate is leading.
Some advantages of using this project:
- HTML is much more accessible than Dominion's PDF reports for people with vision impairment or low vision. For example, screen reader software works better with HTML.
- The HTML generated by this project can be made to support multiple languages, whereas the default reports are English-only.
- The HTML can also be customized in other ways to increase accessibility, readability, and convenience. For example, things like additional highlighting or styling, sorting of rows, and links to additional explanatory material can all be added (like definitions of terms like "continuing ballots" and "exhausted ballots").
- Using HTML snippets lets the results of multiple RCV contests be viewed on a single page. This way members of the public don't need to click to a different page for each contest to see who the winner is and what the final-round vote totals are.
- The tables in Dominion's existing PDF reports can be inconveniently split by page breaks, which makes them harder to read. This can be seen, for example, in the screenshot below of a PDF report from the November 2022 election:
- Using the script in this project doesn't require any changes to how Dominion's voting system is used or configured. It can use the RCV results reports already generated by the system.
To see a demo page, you can go here. The demo shows RCV summary tables for the November 2019, November 2020, February 2022, and November 2022 elections.
The code works like this:
-
First, the code reads in an XML or Excel report for an RCV contest from the Dominion system and extracts the candidate names and vote subtotals for each round. Optionally, this intermediate information can be saved to a JSON file before proceeding to the next step. Here is an example of what such a JSON file looks like (for the same contest from the screenshots above).
-
Second, the code takes the candidate names and vote subtotals from the previous step and generates one or more HTML snippets for the contest (e.g. one for each template and language). These snippets are then saved to individual files. These HTML snippets can then be included in a larger HTML summary page, like they are in the sample demo page.
To generate the HTML snippets, the code uses two Jinja templates located in this directory, and a YAML file located here of translations of words used in the Dominion's original reports (words like "Overvotes," "Exhausted," and "Continuing Ballots"). The templates can be customized as needed to control exactly how the HTML snippets look, and the YAML file can be expanded to support more languages and cover more words. Here is an example of what such an HTML snippet might look like (again for the same contest as above), and here is the directory of all HTML snippets used in the demo for the November 2022 election.
All of the above takes less than a second to run.
This project is intended for use with Dominion's Democracy Suite 5.10A. This is the Dominion voting system that the California Secretary of State approved in July 2020 (with approvals of subsequent modifications in 2021 and later).
Install Python. The project is tested with the following versions of Python:
- Python 3.9
- Python 3.10
- Python 3.11
- Python 3.12
(See here for which Python versions are still current.)
Install Python requirements and the project itself (preferably within a Python virtual environment):
$ pip install -r requirements/requirements.txt
$ pip install -e .
The project contains two scripts:
parse_results.py
: parse XML or Excel RCV result reports generated by the Dominion system, and write the data to JSON files (one per contest).make_reports.py
: read one or more JSON files generated byparse_results.py
above (one per contest), and generate HTML snippets for the contests.
For usage instructions:
$ python src/rcvresults/scripts/parse_results.py --help
For example (this should work from the repo root):
$ python src/rcvresults/scripts/parse_results.py \
--output-dir json-files \
data/input-reports/2019-11-05/*.xml
For usage instructions:
$ python src/rcvresults/scripts/make_reports.py --help
For example (this should work from the repo root):
$ python src/rcvresults/scripts/make_reports.py \
--template-vars config/template-contexts.yml --output-dir final \
data/election-configs/election-2022-11-08.yml translations.yml \
data/demo-json/2022-11-08/*.json
To run tests:
$ python -m unittest discover rcvresults
To rebuild the demo described in the "Demo" section above (includes four elections):
$ python src/rcvresults/scripts/build_demo.py \
--template-vars config/template-contexts-demo-local.yml \
--build-time 2023-09-01T09:00:00
"Tidied" versions of the HTML files in the html
directory were generated
using HTML Tidy.
For example:
$ tidy -output data/election-htmls/2024-03-05/index-tidied.html \
-utf8 data/election-htmls/2024-03-05/index-original.html
BSD 3-Clause License
Copyright (c) 2023, Chris Jerdonek