Skip to content

Elections and recounts

Bjarni Rúnar Einarsson edited this page Aug 11, 2016 · 18 revisions

(This page is current as of August 2016)

The Default Election Process

  1. Users can announce their candidacy up to a certain date/time
  2. At another date/time, elections begin
  3. At the final date/time, elections end
  4. Someone (an admin or a cron job) invokes manage.py processelections:
    1. core.models.Elections.process is invoked
    2. An instance of core.elections.BallotCounter counts the votes
    3. The same core.elections.BallotCounter writes a copy of raw (but anonymized) ballots to a JSON file in the filesystem, the name/path of which are determined by settings.BALLOT_SAVEFILE_FORMAT.
    4. The original ballot data will be deleted from the database
    5. A set of core.elections.ElectionResultRow objects will be created, representing the results

Manual recounts

The file core/elections.py can be invoked as a stand-alone tool and has no Django dependencies. It can be used to recount ballots from a JSON file using different algorithms, and/or omitting certain candidates.

An example of the common case, recounting after one or more candidates have withdrawn from the election:

$ python core/elections.py \
    -e quitter1 -e quitter2 \
    count schulze test_data/condorcet_cycle.json
...

Supported counting methods

At the time of writing, elections.py supports the following voting systems: schulze, stcom, condorcet, stv1, ..., stv5, stv10

Most of these are standard counting methods that you can read about on Wikipedia or elsewhere. The exception is the stcom method: it implements the Icelandic pirate party steering committee election system; currently a combination of schulze and plain condorcet (to verify whether the chairperson is unambiguously elected).

More election.py examples

The built-in help is likely to be more current than this document:

$ python core/elections.py --help
usage: elections.py [-h] [-e EXCLUDE] [--keep-gaps]
                    operation system filenames [filenames ...]

positional arguments:
  operation             Operation to perform (count)
  system                Counting system to use (schulze, stv5, ...)
  filenames             Ballot files to read

optional arguments:
  -h, --help            show this help message and exit
  -e EXCLUDE, --exclude EXCLUDE
                        Candidate(s) to exclude when counting
  --keep-gaps           Preserve gaps if ballots are not sequential

We can use the included test-data to verify that Schulze will arbitrarily break Condorcet ties/cycles; if you run this a few times you'll get different results each time.

$ python core/elections.py count schulze test_data/condorcet_cycle.json 
Voting system:
        Schulze, Ordered list (schulze)

Loaded 3 ballots from:
        test_data/condorcet_cycle.json

Schulze old and new match, hooray.
Results:
        Bjarni, Smari, Bjorn

Counting using the condorcet method should however correctly return no results:

$ python core/elections.py count condorcet test_data/condorcet_cycle.json 
Voting system:
        Condorcet (condorcet)

Loaded 3 ballots from:
        test_data/condorcet_cycle.json

Results:

To break the cycle, we can try omitting one candidate:

python core/elections.py -e Bjorn count condorcet test_data/condorcet_cycle.json 
Voting system:
        Condorcet (condorcet)

Loaded 3 ballots from:
        test_data/condorcet_cycle.json

Results:
        Bjarni

Hooray!

Clone this wiki locally