From 9b697dc698add751a7c29d17784483f29648338c Mon Sep 17 00:00:00 2001
From: millosolomillo <marcomillo@gmail.com>
Date: Thu, 16 Apr 2020 05:24:27 +0200
Subject: [PATCH] Pulling updated benadida master branch (#6)

* [DJ1.11] Use a dict instead of django.template.Context

* [Travis] update travis badge to match current branch

* [Travis] Emit all warnings during tests

* [DJ1.9] Remove warning of not declaring explicit app_label

Example:

RemovedInDjango19Warning: Model class helios.models.Voter doesn't declare an explicit app_label and either isn't in an application in INSTALLED_APPS or else was imported before its application was loaded. This will no longer be supported in Django 1.9.

(cherry picked from commit 693acef9e4c3b093d5bf2bc1655e6f53a6dd4007)

* [DJ1.10] list of urlpatterns is a list of url() instead of a patterns() result

* [DJ1.10] Support for string view arguments to url() will be removed

* [DJ1.9] Add used django.contrib apps in installed apps

* Remove traces of south (now unused)

* Upgrade celery

* [DJ1.9] Fix some imports for django 1.9

* A bit more homogeneous redirect creation

* Improvements to tests

* [DJ1.9] Pre-fix tests for Django 1.9

* Fixed election sorting in admin elections page

* [DJ1.10] Redefine template settings

* [Travis] Upgrade pip instead of fixing setuptools' version

* [DJ1.9] Cannot import models from a django-app init (even as third-party import)

* Use urlparse.parse_qs instead of cgi.parse_qs (deprecated)

* Explicit some more imports removing almost all conflicting 'import *'

* Restore functionality of extract-passwords-for-email

* Upgrade Django to 1.9

* [DJ1.10] SubfieldBase has been deprecated. Use Field.from_db_value instead.

* Silence urls.W002 warning.

Slashes aren't deemed necessary at start of URL patterns, but the actual check author recognized the too-strict approach for his check: https://stackoverflow.com/a/41450355

* [DJ2] on_delete will be a required arg for ForeignKey, currently defaults to models.CASCADE.

* [DJ1.10] Upgrade Celery to 4.2.1, Kombu to 4.2.0

Should probably integrate with django-celery-results and/or django-celery-beat

* [DJ2] SelectDateWidget is moved out of extras

* [tests] Extend setup_login capabilities

* [tests] Add admin user fixture and test accessibility of stats

* [DJ1.10] Update stats_urls to have names and be reversed by name

* [DJ1.10] Update election_urls to have names and be reversed by name

* [DJ1.10] reverse function is now in django.urls

* [DJ1.10] Update helios.urls to have names and be reversed by name

* Make stats_url_names and election_url_names visible from url_names

* [DJ1.10] Update helios_auth.urls to have names and be reversed by name

* [DJ1.10] Upgrade to Django 1.10

* [DJ1.11] Fix widgets

* [DJ1.11] Upgrade to Django 1.11

* [Travis] Test to run travis on both Xenial and Trusty

* [Travis] Postgres 9.3 isn't present in Xenial

* Fix csrf_token on perms_why

* [DJ2] Update HSTS Middleware to latest definition

* Bump Django to 1.11.28

* [DJ1.8+] remove unneeded django-sslify and django-secure

* [celery] Make celery backend configurable

Also only use eager using testing, even if it is not recommended

* [readme] revert travis badge to benadida/helios-server

* add recent significant contributors

Co-authored-by: Marco Ciotola <848222@stud.unive.it>
Co-authored-by: Douglas Cerna (Soy Douglas) <douglascerna@yahoo.com>
Co-authored-by: Ben Adida <ben@adida.net>
---
 .travis.yml                                   |  41 ++-
 CONTRIBUTORS.txt                              |   2 +
 Procfile                                      |   2 +-
 README.md                                     |   2 +-
 extract-passwords-for-email.py                |  12 +-
 helios/__init__.py                            |   6 +-
 helios/apps.py                                |   5 +
 helios/celery_app.py                          |  21 ++
 helios/datatypes/__init__.py                  |  24 +-
 helios/datatypes/djangofield.py               |  24 +-
 helios/datatypes/legacy.py                    |   2 +-
 helios/datetimewidget.py                      |   2 +
 helios/election_url_names.py                  |  62 +++++
 helios/election_urls.py                       | 120 ++++----
 helios/fixtures/users.json                    |  41 ++-
 helios/forms.py                               |   4 +-
 .../commands/helios_trustee_decrypt.py        |   6 +-
 .../management/commands/load_voter_files.py   |  74 ++---
 .../management/commands/verify_cast_votes.py  |   6 +-
 helios/migrations/0001_initial.py             |  16 +-
 helios/models.py                              |  71 +++--
 helios/security.py                            |  24 +-
 helios/south_migrations/0001_initial.py       | 259 ------------------
 ...0002_v3_1_new_election_and_voter_fields.py | 196 -------------
 ...election_specific_voters_with_passwords.py | 178 ------------
 .../0004_v3_1_remove_voter_fields.py          | 153 -----------
 .../0005_add_quarantine_fields.py             | 154 -----------
 ..._unique_voter_voter_login_id_election__.py | 220 ---------------
 ..._file_content__chg_field_voterfile_vote.py | 149 ----------
 ...auto__add_unique_trustee_election_email.py | 143 ----------
 ...009_auto__add_field_election_help_email.py | 144 ----------
 ...d_field_election_randomize_answer_order.py | 146 ----------
 ...o__add_field_election_election_info_url.py | 147 ----------
 ...__add_field_election_result_released_at.py | 148 ----------
 helios/south_migrations/__init__.py           |   0
 helios/stats_url_names.py                     |   5 +
 helios/stats_urls.py                          |  20 +-
 helios/stats_views.py                         |  24 +-
 helios/tasks.py                               |  92 ++++---
 helios/templates/_castconfirm_docast.html     |   2 +-
 helios/templates/_castconfirm_password.html   |   2 +-
 helios/templates/cast_done.html               |   4 +-
 helios/templates/castvote.html                |   2 +-
 helios/templates/combine_decryptions.html     |   2 +-
 .../templates/election_audited_ballots.html   |   4 +-
 helios/templates/election_bboard.html         |  10 +-
 helios/templates/election_build.html          |   2 +-
 helios/templates/election_cast_confirm.html   |   2 +-
 helios/templates/election_compute_tally.html  |   4 +-
 helios/templates/election_edit.html           |   2 +-
 helios/templates/election_extend.html         |   2 +-
 helios/templates/election_freeze.html         |   4 +-
 helios/templates/election_keygenerator.html   |   4 +-
 helios/templates/election_not_started.html    |   2 +-
 helios/templates/election_questions.html      |   2 +-
 helios/templates/election_register.html       |   2 +-
 helios/templates/election_tallied.html        |   2 +-
 helios/templates/election_view.html           |  38 +--
 helios/templates/elections_administered.html  |   2 +-
 helios/templates/elections_voted.html         |   2 +-
 helios/templates/list_trustees.html           |  12 +-
 helios/templates/new_trustee.html             |   2 +-
 helios/templates/release_result.html          |   2 +-
 helios/templates/stats.html                   |   8 +-
 helios/templates/stats_elections.html         |   4 +-
 helios/templates/stats_problem_elections.html |   2 +-
 helios/templates/stats_recent_votes.html      |   2 +-
 helios/templates/trustee_check_sk.html        |   2 +-
 .../templates/trustee_decrypt_and_prove.html  |   2 +-
 helios/templates/trustee_home.html            |   6 +-
 helios/templates/voters_eligibility.html      |   2 +-
 helios/templates/voters_email.html            |   4 +-
 helios/templates/voters_list.html             |  20 +-
 helios/templates/voters_manage.html           |   6 +-
 helios/templates/voters_search.html           |   4 +-
 helios/templates/voters_upload.html           |   2 +-
 helios/templates/voters_upload_confirm.html   |   6 +-
 helios/test.py                                |  25 +-
 helios/tests.py                               | 177 ++++++------
 helios/url_names.py                           |  27 ++
 helios/urls.py                                |  45 ++-
 helios/view_utils.py                          |  51 ++--
 helios/views.py                               |  91 +++---
 helios/widgets.py                             |  31 +--
 helios/workflows/homomorphic.py               |   6 +-
 helios_auth/apps.py                           |   5 +
 helios_auth/auth_systems/cas.py               |  17 +-
 helios_auth/auth_systems/clever.py            |  11 +-
 .../djangofb/default_app/urls.py              |  10 +-
 helios_auth/auth_systems/google.py            |  10 +-
 helios_auth/auth_systems/linkedin.py          |   2 +-
 helios_auth/auth_systems/openid/util.py       |   2 +-
 helios_auth/auth_systems/password.py          |  18 +-
 helios_auth/auth_systems/twitter.py           |  13 +-
 helios_auth/auth_systems/yahoo.py             |   6 +-
 helios_auth/jsonfield.py                      |  28 +-
 helios_auth/models.py                         |   9 +-
 helios_auth/security/__init__.py              |  15 +-
 helios_auth/security/oauth.py                 |  29 +-
 helios_auth/south_migrations/0001_initial.py  |  49 ----
 helios_auth/south_migrations/__init__.py      |   0
 helios_auth/templates/index.html              |   2 +-
 helios_auth/templates/login_box.html          |   4 +-
 helios_auth/templates/perms_why.html          |   2 +-
 helios_auth/tests.py                          |   4 +-
 helios_auth/url_names.py                      |   6 +
 helios_auth/urls.py                           |  22 +-
 helios_auth/view_utils.py                     |  48 ++--
 helios_auth/views.py                          |  44 ++-
 requirements.txt                              |   9 +-
 reset.sh                                      |   2 +-
 server_ui/__init__.py                         |   2 -
 server_ui/glue.py                             |  51 ++--
 server_ui/templates/base.html                 |   8 +-
 server_ui/templates/confirm.html              |   2 +-
 server_ui/templates/done.html                 |   2 +-
 server_ui/templates/election_tallied.html     |   2 +-
 server_ui/templates/index.html                |  14 +-
 server_ui/urls.py                             |  16 +-
 server_ui/view_utils.py                       |  10 +-
 server_ui/views.py                            |  20 +-
 settings.py                                   |  84 +++---
 urls.py                                       |  26 +-
 123 files changed, 1054 insertions(+), 2930 deletions(-)
 create mode 100644 helios/apps.py
 create mode 100644 helios/celery_app.py
 create mode 100644 helios/election_url_names.py
 delete mode 100644 helios/south_migrations/0001_initial.py
 delete mode 100644 helios/south_migrations/0002_v3_1_new_election_and_voter_fields.py
 delete mode 100644 helios/south_migrations/0003_v3_1_election_specific_voters_with_passwords.py
 delete mode 100644 helios/south_migrations/0004_v3_1_remove_voter_fields.py
 delete mode 100644 helios/south_migrations/0005_add_quarantine_fields.py
 delete mode 100644 helios/south_migrations/0006_auto__chg_field_voter_vote__add_unique_voter_voter_login_id_election__.py
 delete mode 100644 helios/south_migrations/0007_auto__add_field_voterfile_voter_file_content__chg_field_voterfile_vote.py
 delete mode 100644 helios/south_migrations/0008_auto__add_unique_trustee_election_email.py
 delete mode 100644 helios/south_migrations/0009_auto__add_field_election_help_email.py
 delete mode 100644 helios/south_migrations/0010_auto__add_field_election_randomize_answer_order.py
 delete mode 100644 helios/south_migrations/0011_auto__add_field_election_election_info_url.py
 delete mode 100644 helios/south_migrations/0012_auto__add_field_election_result_released_at.py
 delete mode 100644 helios/south_migrations/__init__.py
 create mode 100644 helios/stats_url_names.py
 create mode 100644 helios/url_names.py
 create mode 100644 helios_auth/apps.py
 delete mode 100644 helios_auth/south_migrations/0001_initial.py
 delete mode 100644 helios_auth/south_migrations/__init__.py
 create mode 100644 helios_auth/url_names.py

diff --git a/.travis.yml b/.travis.yml
index 6b93e8a..1505fcc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,16 +2,41 @@ sudo: false
 language: python
 python:
   - "2.7"
-# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
+
+os: linux
+
+before_install:
+  - export BOTO_CONFIG=/dev/null
+
 install:
-  - pip install setuptools==24.3.1
+  - pip install --upgrade pip
   - pip install -r requirements.txt
-# command to run tests, e.g. python setup.py test
-script: "python manage.py test"
-addons:
-  postgresql: "9.3"
+
 before_script:
   - psql -c 'create database helios;' -U postgres
-before_install:
-  - export BOTO_CONFIG=/dev/null
 
+script: "python -Wall manage.py test"
+
+jobs:
+  include:
+  - dist: trusty
+    addons:
+      postgresql: "9.3"
+  - dist: trusty
+    addons:
+      postgresql: "9.4"
+  - dist: trusty
+    addons:
+      postgresql: "9.5"
+  - dist: trusty
+    addons:
+      postgresql: "9.6"
+  - dist: xenial
+    addons:
+      postgresql: "9.4"
+  - dist: xenial
+    addons:
+      postgresql: "9.5"
+  - dist: xenial
+    addons:
+      postgresql: "9.6"
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 61ac196..e37f6e4 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -7,3 +7,5 @@ Significant contributors:
 - Olivier de Marneffe
 - Emily Stark, Mike Hamburg, Tom Wu, and Dan Boneh for SJCL and integration of javascript crypto.
 - Nicholas Chang-Fong and Aleksander Essex for security reports and fixes.
+- Shirley Chaves
+- Marco Ciotola
diff --git a/Procfile b/Procfile
index 63d8d0c..608417d 100644
--- a/Procfile
+++ b/Procfile
@@ -1,2 +1,2 @@
 web: gunicorn wsgi:application -b 0.0.0.0:$PORT -w 8
-worker: python manage.py celeryd -E -B --beat --concurrency=1
\ No newline at end of file
+worker: celery worker --app helios --events --beat --concurrency 1 --logfile celeryw.log --pidfile celeryw.pid
\ No newline at end of file
diff --git a/README.md b/README.md
index 5c90a9c..9a5519b 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,6 @@
 
 Helios is an end-to-end verifiable voting system.
 
-![Travis Build Status](https://travis-ci.org/benadida/helios-server.svg?branch=master)
+[![Travis Build Status](https://travis-ci.org/benadida/helios-server.svg?branch=master)](https://travis-ci.org/benadida/helios-server)
 
 [![Stories in Ready](https://badge.waffle.io/benadida/helios-server.png?label=ready&title=Ready)](https://waffle.io/benadida/helios-server)
diff --git a/extract-passwords-for-email.py b/extract-passwords-for-email.py
index d727057..7769d02 100644
--- a/extract-passwords-for-email.py
+++ b/extract-passwords-for-email.py
@@ -5,12 +5,16 @@
 # python extract-passwords-for-email.py <election_uuid> <email_address>
 #
 
-from django.core.management import setup_environ
-import settings, sys, csv
+import sys
 
-setup_environ(settings)
+import csv
+import django
+import os
 
-from helios.models import *
+from helios.models import Election
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
+django.setup()
 
 election_uuid = sys.argv[1]
 email = sys.argv[2]
diff --git a/helios/__init__.py b/helios/__init__.py
index 3a516d7..2f59829 100644
--- a/helios/__init__.py
+++ b/helios/__init__.py
@@ -1,5 +1,9 @@
 from django.conf import settings
-from django.core.urlresolvers import reverse
+# This will make sure the app is always imported when
+# Django starts so that shared_task will use this app.
+from celery_app import app as celery_app
+
+__all__ = ('celery_app', 'TEMPLATE_BASE', 'ADMIN_ONLY', 'VOTERS_UPLOAD', 'VOTERS_EMAIL',)
 
 TEMPLATE_BASE = settings.HELIOS_TEMPLATE_BASE or "helios/templates/base.html"
 
diff --git a/helios/apps.py b/helios/apps.py
new file mode 100644
index 0000000..9009743
--- /dev/null
+++ b/helios/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+class HeliosConfig(AppConfig):
+    name = 'helios'
+    verbose_name = "Helios"
diff --git a/helios/celery_app.py b/helios/celery_app.py
new file mode 100644
index 0000000..89f0ecb
--- /dev/null
+++ b/helios/celery_app.py
@@ -0,0 +1,21 @@
+import os
+
+# set the default Django settings module for the 'celery' program.
+from celery import Celery
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
+
+app = Celery()
+
+# Using a string here means the worker doesn't have to serialize
+# the configuration object to child processes.
+# - namespace='CELERY' means all celery-related configuration keys
+#   should have a `CELERY_` prefix.
+app.config_from_object('django.conf:settings', namespace='CELERY')
+
+# Load task modules from all registered Django app configs.
+app.autodiscover_tasks()
+
+@app.task(bind=True)
+def debug_task(self):
+    print('Request: {0!r}'.format(self.request))
diff --git a/helios/datatypes/__init__.py b/helios/datatypes/__init__.py
index b0ede25..93bca24 100644
--- a/helios/datatypes/__init__.py
+++ b/helios/datatypes/__init__.py
@@ -33,7 +33,7 @@ from helios.crypto import utils as cryptoutils
 ## utility function
 ##
 def recursiveToDict(obj):
-    if obj == None:
+    if obj is None:
         return None
 
     if type(obj) == list:
@@ -53,7 +53,7 @@ def get_class(datatype):
     dynamic_module = __import__(".".join(parsed_datatype[:-1]), globals(), locals(), [], level=-1)
     
     if not dynamic_module:
-        raise Exception("no module for %s" % datatpye)
+        raise Exception("no module for %s" % datatype)
 
     # go down the attributes to get to the class
     try:
@@ -130,7 +130,7 @@ class LDObject(object):
             raise Exception("no datatype found")
 
         # nulls
-        if obj == None:
+        if obj is None:
             return None
 
         # the class
@@ -171,7 +171,7 @@ class LDObject(object):
                 self.structured_fields[f] = sub_ld_object
 
                 # set the field on the wrapped object too
-                if sub_ld_object != None:
+                if sub_ld_object is not None:
                     self._setattr_wrapped(f, sub_ld_object.wrapped_obj)
                 else:
                     self._setattr_wrapped(f, None)
@@ -190,7 +190,7 @@ class LDObject(object):
         fields = self.FIELDS
 
         if not self.structured_fields:
-            if self.wrapped_obj.alias != None:
+            if self.wrapped_obj.alias is not None:
                 fields = self.ALIASED_VOTER_FIELDS
 
         for f in (alternate_fields or fields):
@@ -214,7 +214,7 @@ class LDObject(object):
     @classmethod
     def fromDict(cls, d, type_hint=None):
         # null objects
-        if d == None:
+        if d is None:
             return None
 
         # the LD type is either in d or in type_hint
@@ -248,11 +248,11 @@ class LDObject(object):
         """
         process some fields on the way into the object
         """
-        if field_value == None:
+        if field_value is None:
             return None
       
         val = self._process_value_in(field_name, field_value)
-        if val != None:
+        if val is not None:
             return val
         else:
             return field_value
@@ -264,11 +264,11 @@ class LDObject(object):
         """
         process some fields on the way out of the object
         """
-        if field_value == None:
+        if field_value is None:
             return None
       
         val = self._process_value_out(field_name, field_value)
-        if val != None:
+        if val is not None:
             return val
         else:
             return field_value
@@ -278,9 +278,9 @@ class LDObject(object):
     
     def __eq__(self, other):
         if not hasattr(self, 'uuid'):
-            return super(LDObject,self) == other
+            return super(LDObject, self) == other
     
-        return other != None and self.uuid == other.uuid
+        return other is not None and self.uuid == other.uuid
   
 
 class BaseArrayOfObjects(LDObject):
diff --git a/helios/datatypes/djangofield.py b/helios/datatypes/djangofield.py
index e0eb1b4..05f8cf3 100644
--- a/helios/datatypes/djangofield.py
+++ b/helios/datatypes/djangofield.py
@@ -6,15 +6,12 @@ http://www.djangosnippets.org/snippets/377/
 and adapted to LDObject
 """
 
-import datetime
 import json
 from django.db import models
-from django.db.models import signals
-from django.conf import settings
-from django.core.serializers.json import DjangoJSONEncoder
 
 from . import LDObject
 
+
 class LDObjectField(models.TextField):
     """
     LDObject is a generic textfield that neatly serializes/unserializes
@@ -23,9 +20,6 @@ class LDObjectField(models.TextField):
     deserialization_params added on 2011-01-09 to provide additional hints at deserialization time
     """
 
-    # Used so to_python() is called
-    __metaclass__ = models.SubfieldBase
-
     def __init__(self, type_hint=None, **kwargs):
         self.type_hint = type_hint
         super(LDObjectField, self).__init__(**kwargs)
@@ -37,7 +31,11 @@ class LDObjectField(models.TextField):
         if not isinstance(value, basestring):
             return value
 
-        if  value == None:
+        return self.from_db_value(value)
+
+    # noinspection PyUnusedLocal
+    def from_db_value(self, value, *args, **kwargs):
+        if value is None:
             return None
 
         # in some cases, we're loading an existing array or dict,
@@ -50,9 +48,9 @@ class LDObjectField(models.TextField):
         else:
             parsed_value = value
 
-        if parsed_value != None:
-            "we give the wrapped object back because we're not dealing with serialization types"            
-            return_val = LDObject.fromDict(parsed_value, type_hint = self.type_hint).wrapped_obj
+        if parsed_value is not None:
+            # we give the wrapped object back because we're not dealing with serialization types
+            return_val = LDObject.fromDict(parsed_value, type_hint=self.type_hint).wrapped_obj
             return return_val
         else:
             return None
@@ -62,7 +60,7 @@ class LDObjectField(models.TextField):
         if isinstance(value, basestring):
             return value
 
-        if value == None:
+        if value is None:
             return None
 
         # instantiate the proper LDObject to dump it appropriately
@@ -71,4 +69,4 @@ class LDObjectField(models.TextField):
 
     def value_to_string(self, obj):
         value = self._get_val_from_obj(obj)
-        return self.get_db_prep_value(value)
+        return self.get_db_prep_value(value, None)
diff --git a/helios/datatypes/legacy.py b/helios/datatypes/legacy.py
index c0a24ff..d469b44 100644
--- a/helios/datatypes/legacy.py
+++ b/helios/datatypes/legacy.py
@@ -77,7 +77,7 @@ class Voter(LegacyObject):
         """
         depending on whether the voter is aliased, use different fields
         """
-        if self.wrapped_obj.alias != None:
+        if self.wrapped_obj.alias is not None:
             return super(Voter, self).toDict(self.ALIASED_VOTER_FIELDS, complete = complete)
         else:
             return super(Voter,self).toDict(complete = complete)
diff --git a/helios/datetimewidget.py b/helios/datetimewidget.py
index 5a9e0d4..81d81e0 100644
--- a/helios/datetimewidget.py
+++ b/helios/datetimewidget.py
@@ -25,6 +25,8 @@ calbtn = u'''<img src="%smedia/admin/img/admin/icon_calendar.gif" alt="calendar"
 </script>'''
 
 class DateTimeWidget(forms.widgets.TextInput):
+    template_name = ''
+
     class Media:
         css = {
             'all': (
diff --git a/helios/election_url_names.py b/helios/election_url_names.py
new file mode 100644
index 0000000..eff9a5e
--- /dev/null
+++ b/helios/election_url_names.py
@@ -0,0 +1,62 @@
+ELECTION_HOME="election@home"
+ELECTION_VIEW="election@view"
+ELECTION_META="election@meta"
+ELECTION_EDIT="election@edit"
+ELECTION_SCHEDULE="election@schedule"
+ELECTION_EXTEND="election@extend"
+ELECTION_ARCHIVE="election@archive"
+ELECTION_COPY="election@copy"
+ELECTION_BADGE="election@badge"
+
+ELECTION_TRUSTEES_HOME="election@trustees"
+ELECTION_TRUSTEES_VIEW="election@trustees@view"
+ELECTION_TRUSTEES_NEW="election@trustees@new"
+ELECTION_TRUSTEES_ADD_HELIOS="election@trustees@add-helios"
+ELECTION_TRUSTEES_DELETE="election@trustees@delete"
+
+ELECTION_TRUSTEE_HOME="election@trustee"
+ELECTION_TRUSTEE_SEND_URL="election@trustee@send-url"
+ELECTION_TRUSTEE_KEY_GENERATOR="election@trustee@key-generator"
+ELECTION_TRUSTEE_CHECK_SK="election@trustee@check-sk"
+ELECTION_TRUSTEE_UPLOAD_PK="election@trustee@upload-pk"
+ELECTION_TRUSTEE_DECRYPT_AND_PROVE="election@trustee@decrypt-and-prove"
+ELECTION_TRUSTEE_UPLOAD_DECRYPTION="election@trustee@upload-decryption"
+
+ELECTION_RESULT="election@result"
+ELECTION_RESULT_PROOF="election@result@proof"
+ELECTION_BBOARD="election@bboard"
+ELECTION_AUDITED_BALLOTS="election@audited-ballots"
+
+ELECTION_GET_RANDOMNESS="election@get-randomness"
+ELECTION_ENCRYPT_BALLOT="election@encrypt-ballot"
+ELECTION_QUESTIONS="election@questions"
+ELECTION_SET_REG="election@set-reg"
+ELECTION_SET_FEATURED="election@set-featured"
+ELECTION_SAVE_QUESTIONS="election@save-questions"
+ELECTION_REGISTER="election@register"
+ELECTION_FREEZE="election@freeze"
+
+ELECTION_COMPUTE_TALLY="election@compute-tally"
+ELECTION_COMBINE_DECRYPTIONS="election@combine-decryptions"
+ELECTION_RELEASE_RESULT="election@release-result"
+
+ELECTION_CAST="election@cast"
+ELECTION_CAST_CONFIRM="election@cast-confirm"
+ELECTION_PASSWORD_VOTER_LOGIN="election@password-voter-login"
+ELECTION_CAST_DONE="election@cast-done"
+
+ELECTION_POST_AUDITED_BALLOT="election@post-audited-ballot"
+
+ELECTION_VOTERS_HOME="election@voters"
+ELECTION_VOTERS_UPLOAD="election@voters@upload"
+ELECTION_VOTERS_UPLOAD_CANCEL="election@voters@upload-cancel"
+ELECTION_VOTERS_LIST="election@voters@list"
+ELECTION_VOTERS_LIST_PRETTY="election@voters@list-pretty"
+ELECTION_VOTERS_ELIGIBILITY="election@voters@eligibility"
+ELECTION_VOTERS_EMAIL="election@voters@email"
+ELECTION_VOTER="election@voter"
+ELECTION_VOTER_DELETE="election@voter@delete"
+
+ELECTION_BALLOTS_LIST="election@ballots@list"
+ELECTION_BALLOTS_VOTER="election@ballots@voter"
+ELECTION_BALLOTS_VOTER_LAST="election@ballots@voter@last"
diff --git a/helios/election_urls.py b/helios/election_urls.py
index 6622c55..6fc07ef 100644
--- a/helios/election_urls.py
+++ b/helios/election_urls.py
@@ -4,91 +4,99 @@ Helios URLs for Election related stuff
 Ben Adida (ben@adida.net)
 """
 
-from django.conf.urls import *
+from django.conf.urls import url
 
-from helios.views import *
+from helios import views
+from helios import election_url_names as names
 
-urlpatterns = patterns('',
+urlpatterns = [
     # election data that is cryptographically verified
-    (r'^$', one_election),
+    url(r'^$', views.one_election, name=names.ELECTION_HOME),
 
     # metadata that need not be verified
-    (r'^/meta$', one_election_meta),
+    url(r'^/meta$', views.one_election_meta, name=names.ELECTION_META),
     
     # edit election params
-    (r'^/edit$', one_election_edit),
-    (r'^/schedule$', one_election_schedule),
-    (r'^/extend$', one_election_extend),
-    (r'^/archive$', one_election_archive),
-    (r'^/copy$', one_election_copy),
+    url(r'^/edit$', views.one_election_edit, name=names.ELECTION_EDIT),
+    url(r'^/schedule$', views.one_election_schedule, name=names.ELECTION_SCHEDULE),
+    url(r'^/extend$', views.one_election_extend, name=names.ELECTION_EXTEND),
+    url(r'^/archive$', views.one_election_archive, name=names.ELECTION_ARCHIVE),
+    url(r'^/copy$', views.one_election_copy, name=names.ELECTION_COPY),
 
     # badge
-    (r'^/badge$', election_badge),
+    url(r'^/badge$', views.election_badge, name=names.ELECTION_BADGE),
 
     # adding trustees
-    (r'^/trustees/$', list_trustees),
-    (r'^/trustees/view$', list_trustees_view),
-    (r'^/trustees/new$', new_trustee),
-    (r'^/trustees/add-helios$', new_trustee_helios),
-    (r'^/trustees/delete$', delete_trustee),
+    url(r'^/trustees/$', views.list_trustees, name=names.ELECTION_TRUSTEES_HOME),
+    url(r'^/trustees/view$', views.list_trustees_view, name=names.ELECTION_TRUSTEES_VIEW),
+    url(r'^/trustees/new$', views.new_trustee, name=names.ELECTION_TRUSTEES_NEW),
+    url(r'^/trustees/add-helios$', views.new_trustee_helios, name=names.ELECTION_TRUSTEES_ADD_HELIOS),
+    url(r'^/trustees/delete$', views.delete_trustee, name=names.ELECTION_TRUSTEES_DELETE),
     
     # trustee pages
-    (r'^/trustees/(?P<trustee_uuid>[^/]+)/home$', trustee_home),
-    (r'^/trustees/(?P<trustee_uuid>[^/]+)/sendurl$', trustee_send_url),
-    (r'^/trustees/(?P<trustee_uuid>[^/]+)/keygenerator$', trustee_keygenerator),
-    (r'^/trustees/(?P<trustee_uuid>[^/]+)/check-sk$', trustee_check_sk),
-    (r'^/trustees/(?P<trustee_uuid>[^/]+)/upoad-pk$', trustee_upload_pk),
-    (r'^/trustees/(?P<trustee_uuid>[^/]+)/decrypt-and-prove$', trustee_decrypt_and_prove),
-    (r'^/trustees/(?P<trustee_uuid>[^/]+)/upload-decryption$', trustee_upload_decryption),
+    url(r'^/trustees/(?P<trustee_uuid>[^/]+)/home$',
+        views.trustee_home, name=names.ELECTION_TRUSTEE_HOME),
+    url(r'^/trustees/(?P<trustee_uuid>[^/]+)/sendurl$',
+        views.trustee_send_url, name=names.ELECTION_TRUSTEE_SEND_URL),
+    url(r'^/trustees/(?P<trustee_uuid>[^/]+)/keygenerator$',
+        views.trustee_keygenerator, name=names.ELECTION_TRUSTEE_KEY_GENERATOR),
+    url(r'^/trustees/(?P<trustee_uuid>[^/]+)/check-sk$',
+        views.trustee_check_sk, name=names.ELECTION_TRUSTEE_CHECK_SK),
+    url(r'^/trustees/(?P<trustee_uuid>[^/]+)/upoad-pk$',
+        views.trustee_upload_pk, name=names.ELECTION_TRUSTEE_UPLOAD_PK),
+    url(r'^/trustees/(?P<trustee_uuid>[^/]+)/decrypt-and-prove$',
+        views.trustee_decrypt_and_prove, name=names.ELECTION_TRUSTEE_DECRYPT_AND_PROVE),
+    url(r'^/trustees/(?P<trustee_uuid>[^/]+)/upload-decryption$',
+        views.trustee_upload_decryption, name=names.ELECTION_TRUSTEE_UPLOAD_DECRYPTION),
     
     # election voting-process actions
-    (r'^/view$', one_election_view),
-    (r'^/result$', one_election_result),
-    (r'^/result_proof$', one_election_result_proof),
-    # (r'^/bboard$', one_election_bboard),
-    (r'^/audited-ballots/$', one_election_audited_ballots),
+    url(r'^/view$', views.one_election_view, name=names.ELECTION_VIEW),
+    url(r'^/result$', views.one_election_result, name=names.ELECTION_RESULT),
+    url(r'^/result_proof$', views.one_election_result_proof, name=names.ELECTION_RESULT_PROOF),
+    # url(r'^/bboard$', views.one_election_bboard, name=names.ELECTION_BBOARD),
+    url(r'^/audited-ballots/$', views.one_election_audited_ballots, name=names.ELECTION_AUDITED_BALLOTS),
 
     # get randomness
-    (r'^/get-randomness$', get_randomness),
+    url(r'^/get-randomness$', views.get_randomness, name=names.ELECTION_GET_RANDOMNESS),
 
     # server-side encryption
-    (r'^/encrypt-ballot$', encrypt_ballot),
+    url(r'^/encrypt-ballot$', views.encrypt_ballot, name=names.ELECTION_ENCRYPT_BALLOT),
 
     # construct election
-    (r'^/questions$', one_election_questions),
-    (r'^/set_reg$', one_election_set_reg),
-    (r'^/set_featured$', one_election_set_featured),
-    (r'^/save_questions$', one_election_save_questions),
-    (r'^/register$', one_election_register),
-    (r'^/freeze$', one_election_freeze), # includes freeze_2 as POST target
+    url(r'^/questions$', views.one_election_questions, name=names.ELECTION_QUESTIONS),
+    url(r'^/set_reg$', views.one_election_set_reg, name=names.ELECTION_SET_REG),
+    url(r'^/set_featured$', views.one_election_set_featured, name=names.ELECTION_SET_FEATURED),
+    url(r'^/save_questions$', views.one_election_save_questions, name=names.ELECTION_SAVE_QUESTIONS),
+    url(r'^/register$', views.one_election_register, name=names.ELECTION_REGISTER),
+    url(r'^/freeze$', views.one_election_freeze, name=names.ELECTION_FREEZE), # includes freeze_2 as POST target
     
     # computing tally
-    (r'^/compute_tally$', one_election_compute_tally),
-    (r'^/combine_decryptions$', combine_decryptions),
-    (r'^/release_result$', release_result),
+    url(r'^/compute_tally$', views.one_election_compute_tally, name=names.ELECTION_COMPUTE_TALLY),
+    url(r'^/combine_decryptions$', views.combine_decryptions, name=names.ELECTION_COMBINE_DECRYPTIONS),
+    url(r'^/release_result$', views.release_result, name=names.ELECTION_RELEASE_RESULT),
     
     # casting a ballot before we know who the voter is
-    (r'^/cast$', one_election_cast),
-    (r'^/cast_confirm$', one_election_cast_confirm),
-    (r'^/password_voter_login$', password_voter_login),
-    (r'^/cast_done$', one_election_cast_done),
+    url(r'^/cast$', views.one_election_cast, name=names.ELECTION_CAST),
+    url(r'^/cast_confirm$', views.one_election_cast_confirm, name=names.ELECTION_CAST_CONFIRM),
+    url(r'^/password_voter_login$', views.password_voter_login, name=names.ELECTION_PASSWORD_VOTER_LOGIN),
+    url(r'^/cast_done$', views.one_election_cast_done, name=names.ELECTION_CAST_DONE),
     
     # post audited ballot
-    (r'^/post-audited-ballot', post_audited_ballot),
+    url(r'^/post-audited-ballot', views.post_audited_ballot, name=names.ELECTION_POST_AUDITED_BALLOT),
     
     # managing voters
-    (r'^/voters/$', voter_list),
-    (r'^/voters/upload$', voters_upload),
-    (r'^/voters/upload-cancel$', voters_upload_cancel),
-    (r'^/voters/list$', voters_list_pretty),
-    (r'^/voters/eligibility$', voters_eligibility),
-    (r'^/voters/email$', voters_email),
-    (r'^/voters/(?P<voter_uuid>[^/]+)$', one_voter),
-    (r'^/voters/(?P<voter_uuid>[^/]+)/delete$', voter_delete),
+    url(r'^/voters/$', views.voter_list, name=names.ELECTION_VOTERS_LIST),
+    url(r'^/voters/upload$', views.voters_upload, name=names.ELECTION_VOTERS_UPLOAD),
+    url(r'^/voters/upload-cancel$', views.voters_upload_cancel, name=names.ELECTION_VOTERS_UPLOAD_CANCEL),
+    url(r'^/voters/list$', views.voters_list_pretty, name=names.ELECTION_VOTERS_LIST_PRETTY),
+    url(r'^/voters/eligibility$', views.voters_eligibility, name=names.ELECTION_VOTERS_ELIGIBILITY),
+    url(r'^/voters/email$', views.voters_email, name=names.ELECTION_VOTERS_EMAIL),
+    url(r'^/voters/(?P<voter_uuid>[^/]+)$', views.one_voter, name=names.ELECTION_VOTER),
+    url(r'^/voters/(?P<voter_uuid>[^/]+)/delete$', views.voter_delete, name=names.ELECTION_VOTER_DELETE),
     
     # ballots
-    (r'^/ballots/$', ballot_list),
-    (r'^/ballots/(?P<voter_uuid>[^/]+)/all$', voter_votes),
-    (r'^/ballots/(?P<voter_uuid>[^/]+)/last$', voter_last_vote),
+    url(r'^/ballots/$', views.ballot_list, name=names.ELECTION_BALLOTS_LIST),
+    url(r'^/ballots/(?P<voter_uuid>[^/]+)/all$', views.voter_votes, name=names.ELECTION_BALLOTS_VOTER),
+    url(r'^/ballots/(?P<voter_uuid>[^/]+)/last$', views.voter_last_vote, name=names.ELECTION_BALLOTS_VOTER_LAST),
 
-)
+]
diff --git a/helios/fixtures/users.json b/helios/fixtures/users.json
index c588ce4..c6e18b5 100644
--- a/helios/fixtures/users.json
+++ b/helios/fixtures/users.json
@@ -1 +1,40 @@
-[{"pk": 1, "model": "helios_auth.user", "fields": {"info": "{}", "user_id": "ben@adida.net", "name": "Ben Adida", "user_type": "google", "token": null, "admin_p": false}},{"pk": 2, "model": "helios_auth.user", "fields": {"info": "{}", "user_id": "12345", "name": "Ben Adida", "user_type": "facebook", "token": {"access_token":"1234"}, "admin_p": false}}]
\ No newline at end of file
+[
+  {
+    "pk": 1,
+    "model": "helios_auth.user",
+    "fields": {
+      "info": "{}",
+      "user_id": "ben@adida.net",
+      "name": "Ben Adida",
+      "user_type": "google",
+      "token": null,
+      "admin_p": false
+    }
+  },
+  {
+    "pk": 2,
+    "model": "helios_auth.user",
+    "fields": {
+      "info": "{}",
+      "user_id": "12345",
+      "name": "Ben Adida",
+      "user_type": "facebook",
+      "token": {
+        "access_token": "1234"
+      },
+      "admin_p": false
+    }
+  },
+  {
+    "pk": 3,
+    "model": "helios_auth.user",
+    "fields": {
+      "info": "{}",
+      "user_id": "mccio@github.com",
+      "name": "Marco Ciotola",
+      "user_type": "google",
+      "token": null,
+      "admin_p": true
+    }
+  }
+]
\ No newline at end of file
diff --git a/helios/forms.py b/helios/forms.py
index cb10cfa..86fc18e 100644
--- a/helios/forms.py
+++ b/helios/forms.py
@@ -4,8 +4,8 @@ Forms for Helios
 
 from django import forms
 from models import Election
-from widgets import *
-from fields import *
+from widgets import SplitSelectDateTimeWidget
+from fields import SplitDateTimeField
 from django.conf import settings
 
 
diff --git a/helios/management/commands/helios_trustee_decrypt.py b/helios/management/commands/helios_trustee_decrypt.py
index 3dc75a4..4788330 100644
--- a/helios/management/commands/helios_trustee_decrypt.py
+++ b/helios/management/commands/helios_trustee_decrypt.py
@@ -8,12 +8,10 @@ ben@adida.net
 2010-05-22
 """
 
-from django.core.management.base import BaseCommand, CommandError
-import csv, datetime
+from django.core.management.base import BaseCommand
 
-from helios import utils as helios_utils
+from helios.models import Trustee
 
-from helios.models import *
 
 class Command(BaseCommand):
     args = ''
diff --git a/helios/management/commands/load_voter_files.py b/helios/management/commands/load_voter_files.py
index 5b82285..c34e682 100644
--- a/helios/management/commands/load_voter_files.py
+++ b/helios/management/commands/load_voter_files.py
@@ -8,12 +8,15 @@ ben@adida.net
 2010-05-22
 """
 
-from django.core.management.base import BaseCommand, CommandError
-import csv, datetime
+import datetime
+
+import csv
+import uuid
+from django.core.management.base import BaseCommand
 
 from helios import utils as helios_utils
+from helios.models import User, Voter, VoterFile
 
-from helios.models import *
 
 ##
 ## UTF8 craziness for CSV
@@ -27,42 +30,45 @@ def unicode_csv_reader(unicode_csv_data, dialect=csv.excel, **kwargs):
         # decode UTF-8 back to Unicode, cell by cell:
         yield [unicode(cell, 'utf-8') for cell in row]
 
+
 def utf_8_encoder(unicode_csv_data):
     for line in unicode_csv_data:
         yield line.encode('utf-8')
-  
+
+
 def process_csv_file(election, f):
     reader = unicode_csv_reader(f)
-    
+
     num_voters = 0
     for voter in reader:
-      # bad line
-      if len(voter) < 1:
-        continue
-    
-      num_voters += 1
-      voter_id = voter[0]
-      name = voter_id
-      email = voter_id
-    
-      if len(voter) > 1:
-        email = voter[1]
-    
-      if len(voter) > 2:
-        name = voter[2]
-    
-      # create the user
-      user = User.update_or_create(user_type='password', user_id=voter_id, info = {'password': helios_utils.random_string(10), 'email': email, 'name': name})
-      user.save()
-    
-      # does voter for this user already exist
-      voter = Voter.get_by_election_and_user(election, user)
-    
-      # create the voter
-      if not voter:
-        voter_uuid = str(uuid.uuid1())
-        voter = Voter(uuid= voter_uuid, voter_type = 'password', voter_id = voter_id, name = name, election = election)
-        voter.save()
+        # bad line
+        if len(voter) < 1:
+            continue
+
+        num_voters += 1
+        voter_id = voter[0]
+        name = voter_id
+        email = voter_id
+
+        if len(voter) > 1:
+            email = voter[1]
+
+        if len(voter) > 2:
+            name = voter[2]
+
+        # create the user
+        user = User.update_or_create(user_type='password', user_id=voter_id,
+                                     info={'password': helios_utils.random_string(10), 'email': email, 'name': name})
+        user.save()
+
+        # does voter for this user already exist
+        voter = Voter.get_by_election_and_user(election, user)
+
+        # create the voter
+        if not voter:
+            voter_uuid = str(uuid.uuid1())
+            voter = Voter(uuid=voter_uuid, voter_type='password', voter_id=voter_id, name=name, election=election)
+            voter.save()
 
     return num_voters
 
@@ -70,7 +76,7 @@ def process_csv_file(election, f):
 class Command(BaseCommand):
     args = ''
     help = 'load up voters from unprocessed voter files'
-    
+
     def handle(self, *args, **options):
         # load up the voter files in order of last uploaded
         files_to_process = VoterFile.objects.filter(processing_started_at=None).order_by('uploaded_at')
@@ -86,5 +92,3 @@ class Command(BaseCommand):
             file_to_process.processing_finished_at = datetime.datetime.utcnow()
             file_to_process.num_voters = num_voters
             file_to_process.save()
-            
-            
diff --git a/helios/management/commands/verify_cast_votes.py b/helios/management/commands/verify_cast_votes.py
index 5b7f392..e2fab71 100644
--- a/helios/management/commands/verify_cast_votes.py
+++ b/helios/management/commands/verify_cast_votes.py
@@ -6,12 +6,10 @@ ben@adida.net
 2010-05-22
 """
 
-from django.core.management.base import BaseCommand, CommandError
-import csv, datetime
+from django.core.management.base import BaseCommand
 
-from helios import utils as helios_utils
+from helios.models import CastVote
 
-from helios.models import *
 
 def get_cast_vote_to_verify():
     # fixme: add "select for update" functionality here
diff --git a/helios/migrations/0001_initial.py b/helios/migrations/0001_initial.py
index cb2a41e..8ba6efb 100644
--- a/helios/migrations/0001_initial.py
+++ b/helios/migrations/0001_initial.py
@@ -87,7 +87,7 @@ class Migration(migrations.Migration):
                 ('result_proof', helios_auth.jsonfield.JSONField(null=True)),
                 ('help_email', models.EmailField(max_length=75, null=True)),
                 ('election_info_url', models.CharField(max_length=300, null=True)),
-                ('admin', models.ForeignKey(to='helios_auth.User')),
+                ('admin', models.ForeignKey(to='helios_auth.User', on_delete=models.CASCADE)),
             ],
             options={
                 'abstract': False,
@@ -100,7 +100,7 @@ class Migration(migrations.Migration):
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('log', models.CharField(max_length=500)),
                 ('at', models.DateTimeField(auto_now_add=True)),
-                ('election', models.ForeignKey(to='helios.Election')),
+                ('election', models.ForeignKey(to='helios.Election', on_delete=models.CASCADE)),
             ],
             options={
             },
@@ -120,7 +120,7 @@ class Migration(migrations.Migration):
                 ('pok', helios.datatypes.djangofield.LDObjectField(null=True)),
                 ('decryption_factors', helios.datatypes.djangofield.LDObjectField(null=True)),
                 ('decryption_proofs', helios.datatypes.djangofield.LDObjectField(null=True)),
-                ('election', models.ForeignKey(to='helios.Election')),
+                ('election', models.ForeignKey(to='helios.Election', on_delete=models.CASCADE)),
             ],
             options={
             },
@@ -139,8 +139,8 @@ class Migration(migrations.Migration):
                 ('vote', helios.datatypes.djangofield.LDObjectField(null=True)),
                 ('vote_hash', models.CharField(max_length=100, null=True)),
                 ('cast_at', models.DateTimeField(null=True)),
-                ('election', models.ForeignKey(to='helios.Election')),
-                ('user', models.ForeignKey(to='helios_auth.User', null=True)),
+                ('election', models.ForeignKey(to='helios.Election', on_delete=models.CASCADE)),
+                ('user', models.ForeignKey(to='helios_auth.User', null=True, on_delete=models.CASCADE)),
             ],
             options={
             },
@@ -156,7 +156,7 @@ class Migration(migrations.Migration):
                 ('processing_started_at', models.DateTimeField(null=True)),
                 ('processing_finished_at', models.DateTimeField(null=True)),
                 ('num_voters', models.IntegerField(null=True)),
-                ('election', models.ForeignKey(to='helios.Election')),
+                ('election', models.ForeignKey(to='helios.Election', on_delete=models.CASCADE)),
             ],
             options={
             },
@@ -173,13 +173,13 @@ class Migration(migrations.Migration):
         migrations.AddField(
             model_name='castvote',
             name='voter',
-            field=models.ForeignKey(to='helios.Voter'),
+            field=models.ForeignKey(to='helios.Voter', on_delete=models.CASCADE),
             preserve_default=True,
         ),
         migrations.AddField(
             model_name='auditedballot',
             name='election',
-            field=models.ForeignKey(to='helios.Election'),
+            field=models.ForeignKey(to='helios.Election', on_delete=models.CASCADE),
             preserve_default=True,
         ),
     ]
diff --git a/helios/models.py b/helios/models.py
index 96b6654..4252d16 100644
--- a/helios/models.py
+++ b/helios/models.py
@@ -6,35 +6,33 @@ Ben Adida
 (ben@adida.net)
 """
 
-from django.db import models, transaction
-import json
-from django.conf import settings
-from django.core.mail import send_mail
+import datetime
 
-import datetime, logging, uuid, random, io
 import bleach
+import copy
+import csv
+import io
+import random
+import unicodecsv
+import uuid
+from django.conf import settings
+from django.db import models, transaction
 
-from crypto import electionalgs, algs, utils
-from helios import utils as heliosutils
-import helios.views
-
+from crypto import algs, utils
 from helios import datatypes
-
-
+from helios import utils as heliosutils
+from helios.datatypes.djangofield import LDObjectField
 # useful stuff in helios_auth
-from helios_auth.models import User, AUTH_SYSTEMS
 from helios_auth.jsonfield import JSONField
-from helios.datatypes.djangofield import LDObjectField
+from helios_auth.models import User, AUTH_SYSTEMS
 
-import csv, copy
-import unicodecsv
 
 class HeliosModel(models.Model, datatypes.LDObjectContainer):
   class Meta:
     abstract = True
 
 class Election(HeliosModel):
-  admin = models.ForeignKey(User)
+  admin = models.ForeignKey(User, on_delete=models.CASCADE)
   
   uuid = models.CharField(max_length=50, null=False)
 
@@ -145,6 +143,9 @@ class Election(HeliosModel):
   # downloadable election info
   election_info_url = models.CharField(max_length=300, null=True)
 
+  class Meta:
+    app_label = 'helios'
+
   # metadata for the election
   @property
   def metadata(self):
@@ -575,6 +576,7 @@ class Election(HeliosModel):
 
   @property
   def url(self):
+    import helios.views
     return helios.views.get_election_url(self)
 
   def init_tally(self):
@@ -649,7 +651,8 @@ class Election(HeliosModel):
       prettified_result.append({'question': q['short_name'], 'answers': pretty_question})
 
     return prettified_result
-    
+
+
 class ElectionLog(models.Model):
   """
   a log of events for an election
@@ -659,10 +662,13 @@ class ElectionLog(models.Model):
   VOTER_FILE_ADDED = "voter file added"
   DECRYPTIONS_COMBINED = "decryptions combined"
 
-  election = models.ForeignKey(Election)
+  election = models.ForeignKey(Election, on_delete=models.CASCADE)
   log = models.CharField(max_length=500)
   at = models.DateTimeField(auto_now_add=True)
 
+  class Meta:
+    app_label = 'helios'
+
 ##
 ## UTF8 craziness for CSV
 ##
@@ -691,7 +697,7 @@ class VoterFile(models.Model):
   # path where we store voter upload 
   PATH = settings.VOTER_UPLOAD_REL_PATH
 
-  election = models.ForeignKey(Election)
+  election = models.ForeignKey(Election, on_delete=models.CASCADE)
 
   # we move to storing the content in the DB
   voter_file = models.FileField(upload_to=PATH, max_length=250,null=True)
@@ -702,6 +708,9 @@ class VoterFile(models.Model):
   processing_finished_at = models.DateTimeField(auto_now_add=False, null=True)
   num_voters = models.IntegerField(null=True)
 
+  class Meta:
+    app_label = 'helios'
+
   def itervoters(self):
     if self.voter_file_content:
       if type(self.voter_file_content) == unicode:
@@ -779,10 +788,9 @@ class VoterFile(models.Model):
 
     return num_voters
 
-
     
 class Voter(HeliosModel):
-  election = models.ForeignKey(Election)
+  election = models.ForeignKey(Election, on_delete=models.CASCADE)
   
   # let's link directly to the user now
   # FIXME: delete this as soon as migrations are set up
@@ -794,7 +802,7 @@ class Voter(HeliosModel):
 
   # for users of type password, no user object is created
   # but a dynamic user object is created automatically
-  user = models.ForeignKey('helios_auth.User', null=True)
+  user = models.ForeignKey('helios_auth.User', null=True, on_delete=models.CASCADE)
 
   # if user is null, then you need a voter login ID and password
   voter_login_id = models.CharField(max_length = 100, null=True)
@@ -813,6 +821,7 @@ class Voter(HeliosModel):
 
   class Meta:
     unique_together = (('election', 'voter_login_id'))
+    app_label = 'helios'
 
   def __init__(self, *args, **kwargs):
     super(Voter, self).__init__(*args, **kwargs)
@@ -979,7 +988,7 @@ class Voter(HeliosModel):
   
 class CastVote(HeliosModel):
   # the reference to the voter provides the voter_uuid
-  voter = models.ForeignKey(Voter)
+  voter = models.ForeignKey(Voter, on_delete=models.CASCADE)
   
   # the actual encrypted vote
   vote = LDObjectField(type_hint = 'legacy/EncryptedVote')
@@ -1003,6 +1012,9 @@ class CastVote(HeliosModel):
   # auditing purposes, like too many votes from the same IP, if it isn't expected
   cast_ip = models.GenericIPAddressField(null=True)
 
+  class Meta:
+      app_label = 'helios'
+
   @property
   def datatype(self):
     return self.voter.datatype.replace('Voter', 'CastVote')
@@ -1086,11 +1098,14 @@ class AuditedBallot(models.Model):
   """
   ballots for auditing
   """
-  election = models.ForeignKey(Election)
+  election = models.ForeignKey(Election, on_delete=models.CASCADE)
   raw_vote = models.TextField()
   vote_hash = models.CharField(max_length=100)
   added_at = models.DateTimeField(auto_now_add=True)
 
+  class Meta:
+    app_label = 'helios'
+
   @classmethod
   def get(cls, election, vote_hash):
     return cls.objects.get(election = election, vote_hash = vote_hash)
@@ -1107,9 +1122,10 @@ class AuditedBallot(models.Model):
       query = query[:limit]
 
     return query
-    
+
+
 class Trustee(HeliosModel):
-  election = models.ForeignKey(Election)
+  election = models.ForeignKey(Election, on_delete=models.CASCADE)
   
   uuid = models.CharField(max_length=50)
   name = models.CharField(max_length=200)
@@ -1140,7 +1156,8 @@ class Trustee(HeliosModel):
 
   class Meta:
     unique_together = (('election', 'email'))
-    
+    app_label = 'helios'
+
   def save(self, *args, **kwargs):
     """
     override this just to get a hook
diff --git a/helios/security.py b/helios/security.py
index c022821..88dc675 100644
--- a/helios/security.py
+++ b/helios/security.py
@@ -7,12 +7,12 @@ Ben Adida (ben@adida.net)
 # nicely update the wrapper function
 from functools import update_wrapper
 
-from django.core.urlresolvers import reverse
-from django.core.exceptions import *
-from django.http import *
+from django.urls import reverse
+from django.core.exceptions import PermissionDenied
+from django.http import Http404
 from django.conf import settings
 
-from models import *
+from models import Voter, Trustee, Election
 from helios_auth.security import get_user
 
 from django.http import HttpResponseRedirect
@@ -22,11 +22,23 @@ import helios
 
 
 class HSTSMiddleware:
-    def process_response(self, request, response):
+    def __init__(self, get_response):
+        self.get_response = get_response
+        # One-time configuration and initialization.
+
+    def __call__(self, request):
+        # Code to be executed for each request before
+        # the view (and later middleware) are called.
+
+        response = self.get_response(request)
+
+        # Code to be executed for each request/response after
+        # the view is called.
+
         if settings.STS:
           response['Strict-Transport-Security'] = "max-age=31536000; includeSubDomains; preload"
         return response
-        
+
 # current voter
 def get_voter(request, user, election):
   """
diff --git a/helios/south_migrations/0001_initial.py b/helios/south_migrations/0001_initial.py
deleted file mode 100644
index 4fa118c..0000000
--- a/helios/south_migrations/0001_initial.py
+++ /dev/null
@@ -1,259 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding model 'Election'
-        db.create_table('helios_election', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('admin', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios_auth.User'])),
-            ('uuid', self.gf('django.db.models.fields.CharField')(max_length=50)),
-            ('short_name', self.gf('django.db.models.fields.CharField')(max_length=100)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=250)),
-            ('description', self.gf('django.db.models.fields.TextField')()),
-            ('public_key', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-            ('private_key', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-            ('questions', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-            ('eligibility', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-            ('openreg', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('featured_p', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('use_voter_aliases', self.gf('django.db.models.fields.BooleanField')(default=False)),
-            ('cast_url', self.gf('django.db.models.fields.CharField')(max_length=500)),
-            ('created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
-            ('modified_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
-            ('frozen_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)),
-            ('archived_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)),
-            ('registration_starts_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)),
-            ('voting_starts_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)),
-            ('voting_ends_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)),
-            ('tallying_starts_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)),
-            ('voting_started_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)),
-            ('voting_extended_until', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)),
-            ('voting_ended_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)),
-            ('tallying_started_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)),
-            ('tallying_finished_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)),
-            ('tallies_combined_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True)),
-            ('voters_hash', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)),
-            ('encrypted_tally', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-            ('result', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-            ('result_proof', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-        ))
-        db.send_create_signal('helios', ['Election'])
-
-        # Adding model 'ElectionLog'
-        db.create_table('helios_electionlog', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('election', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios.Election'])),
-            ('log', self.gf('django.db.models.fields.CharField')(max_length=500)),
-            ('at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
-        ))
-        db.send_create_signal('helios', ['ElectionLog'])
-
-        # Adding model 'VoterFile'
-        db.create_table('helios_voterfile', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('election', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios.Election'])),
-            ('voter_file', self.gf('django.db.models.fields.files.FileField')(max_length=250)),
-            ('uploaded_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
-            ('processing_started_at', self.gf('django.db.models.fields.DateTimeField')(null=True)),
-            ('processing_finished_at', self.gf('django.db.models.fields.DateTimeField')(null=True)),
-            ('num_voters', self.gf('django.db.models.fields.IntegerField')(null=True)),
-        ))
-        db.send_create_signal('helios', ['VoterFile'])
-
-        # Adding model 'Voter'
-        db.create_table('helios_voter', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('election', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios.Election'])),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)),
-            ('voter_type', self.gf('django.db.models.fields.CharField')(max_length=100)),
-            ('voter_id', self.gf('django.db.models.fields.CharField')(max_length=100)),
-            ('uuid', self.gf('django.db.models.fields.CharField')(max_length=50)),
-            ('alias', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)),
-            ('vote', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-            ('vote_hash', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)),
-            ('cast_at', self.gf('django.db.models.fields.DateTimeField')(null=True)),
-        ))
-        db.send_create_signal('helios', ['Voter'])
-
-        # Adding model 'CastVote'
-        db.create_table('helios_castvote', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('voter', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios.Voter'])),
-            ('vote', self.gf('helios_auth.jsonfield.JSONField')()),
-            ('vote_hash', self.gf('django.db.models.fields.CharField')(max_length=100)),
-            ('cast_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
-            ('verified_at', self.gf('django.db.models.fields.DateTimeField')(null=True)),
-            ('invalidated_at', self.gf('django.db.models.fields.DateTimeField')(null=True)),
-        ))
-        db.send_create_signal('helios', ['CastVote'])
-
-        # Adding model 'AuditedBallot'
-        db.create_table('helios_auditedballot', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('election', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios.Election'])),
-            ('raw_vote', self.gf('django.db.models.fields.TextField')()),
-            ('vote_hash', self.gf('django.db.models.fields.CharField')(max_length=100)),
-            ('added_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
-        ))
-        db.send_create_signal('helios', ['AuditedBallot'])
-
-        # Adding model 'Trustee'
-        db.create_table('helios_trustee', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('election', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios.Election'])),
-            ('uuid', self.gf('django.db.models.fields.CharField')(max_length=50)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=200)),
-            ('email', self.gf('django.db.models.fields.EmailField')(max_length=75)),
-            ('secret', self.gf('django.db.models.fields.CharField')(max_length=100)),
-            ('public_key', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-            ('public_key_hash', self.gf('django.db.models.fields.CharField')(max_length=100)),
-            ('secret_key', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-            ('pok', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-            ('decryption_factors', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-            ('decryption_proofs', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-        ))
-        db.send_create_signal('helios', ['Trustee'])
-
-
-    def backwards(self, orm):
-        
-        # Deleting model 'Election'
-        db.delete_table('helios_election')
-
-        # Deleting model 'ElectionLog'
-        db.delete_table('helios_electionlog')
-
-        # Deleting model 'VoterFile'
-        db.delete_table('helios_voterfile')
-
-        # Deleting model 'Voter'
-        db.delete_table('helios_voter')
-
-        # Deleting model 'CastVote'
-        db.delete_table('helios_castvote')
-
-        # Deleting model 'AuditedBallot'
-        db.delete_table('helios_auditedballot')
-
-        # Deleting model 'Trustee'
-        db.delete_table('helios_trustee')
-
-
-    models = {
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.auditedballot': {
-            'Meta': {'object_name': 'AuditedBallot'},
-            'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'raw_vote': ('django.db.models.fields.TextField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.castvote': {
-            'Meta': {'object_name': 'CastVote'},
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'vote': ('helios_auth.jsonfield.JSONField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"})
-        },
-        'helios.election': {
-            'Meta': {'object_name': 'Election'},
-            'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}),
-            'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {}),
-            'eligibility': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'encrypted_tally': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
-            'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'private_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'questions': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'result': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
-        },
-        'helios.electionlog': {
-            'Meta': {'object_name': 'ElectionLog'},
-            'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'log': ('django.db.models.fields.CharField', [], {'max_length': '500'})
-        },
-        'helios.trustee': {
-            'Meta': {'object_name': 'Trustee'},
-            'decryption_factors': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'decryption_proofs': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
-            'pok': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.voter': {
-            'Meta': {'object_name': 'Voter'},
-            'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'vote': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'voter_type': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.voterfile': {
-            'Meta': {'object_name': 'VoterFile'},
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
-            'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'})
-        }
-    }
-
-    complete_apps = ['helios']
diff --git a/helios/south_migrations/0002_v3_1_new_election_and_voter_fields.py b/helios/south_migrations/0002_v3_1_new_election_and_voter_fields.py
deleted file mode 100644
index a5d4e1d..0000000
--- a/helios/south_migrations/0002_v3_1_new_election_and_voter_fields.py
+++ /dev/null
@@ -1,196 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding field 'Voter.user'
-        db.add_column('helios_voter', 'user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['helios_auth.User'], null=True), keep_default=False)
-
-        # Adding field 'Voter.voter_login_id'
-        db.add_column('helios_voter', 'voter_login_id', self.gf('django.db.models.fields.CharField')(max_length=100, null=True), keep_default=False)
-
-        # Adding field 'Voter.voter_password'
-        db.add_column('helios_voter', 'voter_password', self.gf('django.db.models.fields.CharField')(max_length=100, null=True), keep_default=False)
-
-        # Adding field 'Voter.voter_name'
-        db.add_column('helios_voter', 'voter_name', self.gf('django.db.models.fields.CharField')(max_length=200, null=True), keep_default=False)
-
-        # Adding field 'Voter.voter_email'
-        db.add_column('helios_voter', 'voter_email', self.gf('django.db.models.fields.CharField')(max_length=250, null=True), keep_default=False)
-
-        # Adding field 'Election.datatype'
-        db.add_column('helios_election', 'datatype', self.gf('django.db.models.fields.CharField')(default='legacy/Election', max_length=250), keep_default=False)
-
-        # Adding field 'Election.election_type'
-        db.add_column('helios_election', 'election_type', self.gf('django.db.models.fields.CharField')(default='election', max_length=250), keep_default=False)
-
-        # Adding field 'Election.private_p'
-        db.add_column('helios_election', 'private_p', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
-
-        # Adding field 'Election.use_advanced_audit_features'
-        db.add_column('helios_election', 'use_advanced_audit_features', self.gf('django.db.models.fields.BooleanField')(default=True), keep_default=False)
-
-        # Adding field 'CastVote.vote_tinyhash'
-        db.add_column('helios_castvote', 'vote_tinyhash', self.gf('django.db.models.fields.CharField')(max_length=50, unique=True, null=True), keep_default=False)
-
-
-    def backwards(self, orm):
-        
-        # Deleting field 'Voter.user'
-        db.delete_column('helios_voter', 'user_id')
-
-        # Deleting field 'Voter.voter_login_id'
-        db.delete_column('helios_voter', 'voter_login_id')
-
-        # Deleting field 'Voter.voter_password'
-        db.delete_column('helios_voter', 'voter_password')
-
-        # Deleting field 'Voter.voter_name'
-        db.delete_column('helios_voter', 'voter_name')
-
-        # Deleting field 'Voter.voter_email'
-        db.delete_column('helios_voter', 'voter_email')
-
-        # Deleting field 'Election.datatype'
-        db.delete_column('helios_election', 'datatype')
-
-        # Deleting field 'Election.election_type'
-        db.delete_column('helios_election', 'election_type')
-
-        # Deleting field 'Election.private_p'
-        db.delete_column('helios_election', 'private_p')
-
-        # Deleting field 'Election.use_advanced_audit_features'
-        db.delete_column('helios_election', 'use_advanced_audit_features')
-
-        # Deleting field 'CastVote.vote_tinyhash'
-        db.delete_column('helios_castvote', 'vote_tinyhash')
-
-
-    models = {
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.auditedballot': {
-            'Meta': {'object_name': 'AuditedBallot'},
-            'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'raw_vote': ('django.db.models.fields.TextField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.castvote': {
-            'Meta': {'object_name': 'CastVote'},
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'vote': ('helios_auth.jsonfield.JSONField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}),
-            'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"})
-        },
-        'helios.election': {
-            'Meta': {'object_name': 'Election'},
-            'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}),
-            'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}),
-            'description': ('django.db.models.fields.TextField', [], {}),
-            'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}),
-            'eligibility': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'encrypted_tally': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
-            'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'private_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'questions': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'result': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
-        },
-        'helios.electionlog': {
-            'Meta': {'object_name': 'ElectionLog'},
-            'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'log': ('django.db.models.fields.CharField', [], {'max_length': '500'})
-        },
-        'helios.trustee': {
-            'Meta': {'object_name': 'Trustee'},
-            'decryption_factors': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'decryption_proofs': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
-            'pok': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.voter': {
-            'Meta': {'object_name': 'Voter'},
-            'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'vote': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_type': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.voterfile': {
-            'Meta': {'object_name': 'VoterFile'},
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
-            'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'})
-        }
-    }
-
-    complete_apps = ['helios']
diff --git a/helios/south_migrations/0003_v3_1_election_specific_voters_with_passwords.py b/helios/south_migrations/0003_v3_1_election_specific_voters_with_passwords.py
deleted file mode 100644
index 573279f..0000000
--- a/helios/south_migrations/0003_v3_1_election_specific_voters_with_passwords.py
+++ /dev/null
@@ -1,178 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import DataMigration
-from django.db import models
-
-class Migration(DataMigration):
-
-    def forwards(self, orm):
-        """
-        update the voters data objects to point to users when it makes sense,
-        and otherwise to copy the data needed from the users table.
-        make all elections legacy, because before now they are.
-        """
-        for e in orm.Election.objects.all():
-            e.datatype = 'legacy/Election'
-            e.save()
-
-        # use the .iterator() call to reduce caching and make this more efficient
-        # so as not to trigger a memory error
-        for v in orm.Voter.objects.all().iterator():
-            user = orm['helios_auth.User'].objects.get(user_type = v.voter_type, user_id = v.voter_id)
-
-            if v.voter_type == 'password':
-                v.voter_login_id = v.voter_id
-                v.voter_name = v.name
-
-                v.voter_email = user.info['email']
-                v.voter_password = user.info['password']
-            else:
-                v.user = user
-
-            v.save()
-
-        # also, update tinyhash for all votes
-        for cv in orm.CastVote.objects.all().iterator():
-            safe_hash = cv.vote_hash
-            for c in ['/', '+']:
-                safe_hash = safe_hash.replace(c,'')
-    
-            length = 8
-            while True:
-                vote_tinyhash = safe_hash[:length]
-                if orm.CastVote.objects.filter(vote_tinyhash = vote_tinyhash).count() == 0:
-                    break
-                length += 1
-      
-            cv.vote_tinyhash = vote_tinyhash
-            cv.save()
-
-
-    def backwards(self, orm):
-        "Write your backwards methods here."
-        raise Exception("can't revert to system-wide user passwords, rather than election specific")
-
-
-    models = {
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.auditedballot': {
-            'Meta': {'object_name': 'AuditedBallot'},
-            'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'raw_vote': ('django.db.models.fields.TextField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.castvote': {
-            'Meta': {'object_name': 'CastVote'},
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'vote': ('helios_auth.jsonfield.JSONField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}),
-            'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"})
-        },
-        'helios.election': {
-            'Meta': {'object_name': 'Election'},
-            'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}),
-            'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}),
-            'description': ('django.db.models.fields.TextField', [], {}),
-            'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}),
-            'eligibility': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'encrypted_tally': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
-            'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'private_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'questions': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'result': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
-        },
-        'helios.electionlog': {
-            'Meta': {'object_name': 'ElectionLog'},
-            'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'log': ('django.db.models.fields.CharField', [], {'max_length': '500'})
-        },
-        'helios.trustee': {
-            'Meta': {'object_name': 'Trustee'},
-            'decryption_factors': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'decryption_proofs': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
-            'pok': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.voter': {
-            'Meta': {'object_name': 'Voter'},
-            'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'vote': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_type': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.voterfile': {
-            'Meta': {'object_name': 'VoterFile'},
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
-            'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'})
-        }
-    }
-
-    complete_apps = ['helios']
diff --git a/helios/south_migrations/0004_v3_1_remove_voter_fields.py b/helios/south_migrations/0004_v3_1_remove_voter_fields.py
deleted file mode 100644
index b934e94..0000000
--- a/helios/south_migrations/0004_v3_1_remove_voter_fields.py
+++ /dev/null
@@ -1,153 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Deleting field 'Voter.name'
-        db.delete_column('helios_voter', 'name')
-
-        # Deleting field 'Voter.voter_id'
-        db.delete_column('helios_voter', 'voter_id')
-
-        # Deleting field 'Voter.voter_type'
-        db.delete_column('helios_voter', 'voter_type')
-
-
-    def backwards(self, orm):
-        
-        # Adding field 'Voter.name'
-        db.add_column('helios_voter', 'name', self.gf('django.db.models.fields.CharField')(max_length=200, null=True), keep_default=False)
-
-        # We cannot add back in field 'Voter.voter_id'
-        raise RuntimeError(
-            "Cannot reverse this migration. 'Voter.voter_id' and its values cannot be restored.")
-
-        # We cannot add back in field 'Voter.voter_type'
-        raise RuntimeError(
-            "Cannot reverse this migration. 'Voter.voter_type' and its values cannot be restored.")
-
-
-    models = {
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.auditedballot': {
-            'Meta': {'object_name': 'AuditedBallot'},
-            'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'raw_vote': ('django.db.models.fields.TextField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.castvote': {
-            'Meta': {'object_name': 'CastVote'},
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'vote': ('helios_auth.jsonfield.JSONField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}),
-            'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"})
-        },
-        'helios.election': {
-            'Meta': {'object_name': 'Election'},
-            'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}),
-            'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}),
-            'description': ('django.db.models.fields.TextField', [], {}),
-            'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}),
-            'eligibility': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'encrypted_tally': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
-            'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'private_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'questions': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'result': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
-        },
-        'helios.electionlog': {
-            'Meta': {'object_name': 'ElectionLog'},
-            'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'log': ('django.db.models.fields.CharField', [], {'max_length': '500'})
-        },
-        'helios.trustee': {
-            'Meta': {'object_name': 'Trustee'},
-            'decryption_factors': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'decryption_proofs': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
-            'pok': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.voter': {
-            'Meta': {'object_name': 'Voter'},
-            'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'vote': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
-        },
-        'helios.voterfile': {
-            'Meta': {'object_name': 'VoterFile'},
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
-            'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'})
-        }
-    }
-
-    complete_apps = ['helios']
diff --git a/helios/south_migrations/0005_add_quarantine_fields.py b/helios/south_migrations/0005_add_quarantine_fields.py
deleted file mode 100644
index b0f9f1d..0000000
--- a/helios/south_migrations/0005_add_quarantine_fields.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding field 'Election.complaint_period_ends_at'
-        db.add_column('helios_election', 'complaint_period_ends_at', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True), keep_default=False)
-
-        # Adding field 'CastVote.quarantined_p'
-        db.add_column('helios_castvote', 'quarantined_p', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
-
-        # Adding field 'CastVote.released_from_quarantine_at'
-        db.add_column('helios_castvote', 'released_from_quarantine_at', self.gf('django.db.models.fields.DateTimeField')(null=True), keep_default=False)
-
-
-    def backwards(self, orm):
-        
-        # Deleting field 'Election.complaint_period_ends_at'
-        db.delete_column('helios_election', 'complaint_period_ends_at')
-
-        # Deleting field 'CastVote.quarantined_p'
-        db.delete_column('helios_castvote', 'quarantined_p')
-
-        # Deleting field 'CastVote.released_from_quarantine_at'
-        db.delete_column('helios_castvote', 'released_from_quarantine_at')
-
-
-    models = {
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.auditedballot': {
-            'Meta': {'object_name': 'AuditedBallot'},
-            'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'raw_vote': ('django.db.models.fields.TextField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.castvote': {
-            'Meta': {'object_name': 'CastVote'},
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'vote': ('helios_auth.jsonfield.JSONField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}),
-            'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"})
-        },
-        'helios.election': {
-            'Meta': {'object_name': 'Election'},
-            'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}),
-            'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
-            'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}),
-            'description': ('django.db.models.fields.TextField', [], {}),
-            'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}),
-            'eligibility': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'encrypted_tally': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
-            'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'private_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'questions': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'result': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
-        },
-        'helios.electionlog': {
-            'Meta': {'object_name': 'ElectionLog'},
-            'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'log': ('django.db.models.fields.CharField', [], {'max_length': '500'})
-        },
-        'helios.trustee': {
-            'Meta': {'object_name': 'Trustee'},
-            'decryption_factors': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'decryption_proofs': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
-            'pok': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'public_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret_key': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.voter': {
-            'Meta': {'object_name': 'Voter'},
-            'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'vote': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
-        },
-        'helios.voterfile': {
-            'Meta': {'object_name': 'VoterFile'},
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
-            'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'})
-        }
-    }
-
-    complete_apps = ['helios']
diff --git a/helios/south_migrations/0006_auto__chg_field_voter_vote__add_unique_voter_voter_login_id_election__.py b/helios/south_migrations/0006_auto__chg_field_voter_vote__add_unique_voter_voter_login_id_election__.py
deleted file mode 100644
index 2791e49..0000000
--- a/helios/south_migrations/0006_auto__chg_field_voter_vote__add_unique_voter_voter_login_id_election__.py
+++ /dev/null
@@ -1,220 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Changing field 'Voter.vote'
-        db.alter_column('helios_voter', 'vote', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True))
-
-        # Adding unique constraint on 'Voter', fields ['voter_login_id', 'election']
-        db.create_unique('helios_voter', ['voter_login_id', 'election_id'])
-
-        # Changing field 'Election.result'
-        db.alter_column('helios_election', 'result', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True))
-
-        # Changing field 'Election.questions'
-        db.alter_column('helios_election', 'questions', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True))
-
-        # Changing field 'Election.encrypted_tally'
-        db.alter_column('helios_election', 'encrypted_tally', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True))
-
-        # Changing field 'Election.eligibility'
-        db.alter_column('helios_election', 'eligibility', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True))
-
-        # Changing field 'Election.private_key'
-        db.alter_column('helios_election', 'private_key', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True))
-
-        # Changing field 'Election.public_key'
-        db.alter_column('helios_election', 'public_key', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True))
-
-        # Changing field 'Trustee.public_key'
-        db.alter_column('helios_trustee', 'public_key', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True))
-
-        # Changing field 'Trustee.decryption_proofs'
-        db.alter_column('helios_trustee', 'decryption_proofs', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True))
-
-        # Changing field 'Trustee.pok'
-        db.alter_column('helios_trustee', 'pok', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True))
-
-        # Changing field 'Trustee.secret_key'
-        db.alter_column('helios_trustee', 'secret_key', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True))
-
-        # Changing field 'Trustee.decryption_factors'
-        db.alter_column('helios_trustee', 'decryption_factors', self.gf('helios.datatypes.djangofield.LDObjectField')(null=True))
-
-        # Changing field 'CastVote.vote'
-        db.alter_column('helios_castvote', 'vote', self.gf('helios.datatypes.djangofield.LDObjectField')())
-
-
-    def backwards(self, orm):
-        
-        # Removing unique constraint on 'Voter', fields ['voter_login_id', 'election']
-        db.delete_unique('helios_voter', ['voter_login_id', 'election_id'])
-
-        # Changing field 'Voter.vote'
-        db.alter_column('helios_voter', 'vote', self.gf('helios_auth.jsonfield.JSONField')(null=True))
-
-        # Changing field 'Election.result'
-        db.alter_column('helios_election', 'result', self.gf('helios_auth.jsonfield.JSONField')(null=True))
-
-        # Changing field 'Election.questions'
-        db.alter_column('helios_election', 'questions', self.gf('helios_auth.jsonfield.JSONField')(null=True))
-
-        # Changing field 'Election.encrypted_tally'
-        db.alter_column('helios_election', 'encrypted_tally', self.gf('helios_auth.jsonfield.JSONField')(null=True))
-
-        # Changing field 'Election.eligibility'
-        db.alter_column('helios_election', 'eligibility', self.gf('helios_auth.jsonfield.JSONField')(null=True))
-
-        # Changing field 'Election.private_key'
-        db.alter_column('helios_election', 'private_key', self.gf('helios_auth.jsonfield.JSONField')(null=True))
-
-        # Changing field 'Election.public_key'
-        db.alter_column('helios_election', 'public_key', self.gf('helios_auth.jsonfield.JSONField')(null=True))
-
-        # Changing field 'Trustee.public_key'
-        db.alter_column('helios_trustee', 'public_key', self.gf('helios_auth.jsonfield.JSONField')(null=True))
-
-        # Changing field 'Trustee.decryption_proofs'
-        db.alter_column('helios_trustee', 'decryption_proofs', self.gf('helios_auth.jsonfield.JSONField')(null=True))
-
-        # Changing field 'Trustee.pok'
-        db.alter_column('helios_trustee', 'pok', self.gf('helios_auth.jsonfield.JSONField')(null=True))
-
-        # Changing field 'Trustee.secret_key'
-        db.alter_column('helios_trustee', 'secret_key', self.gf('helios_auth.jsonfield.JSONField')(null=True))
-
-        # Changing field 'Trustee.decryption_factors'
-        db.alter_column('helios_trustee', 'decryption_factors', self.gf('helios_auth.jsonfield.JSONField')(null=True))
-
-        # Changing field 'CastVote.vote'
-        db.alter_column('helios_castvote', 'vote', self.gf('helios_auth.jsonfield.JSONField')())
-
-
-    models = {
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.auditedballot': {
-            'Meta': {'object_name': 'AuditedBallot'},
-            'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'raw_vote': ('django.db.models.fields.TextField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.castvote': {
-            'Meta': {'object_name': 'CastVote'},
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}),
-            'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"})
-        },
-        'helios.election': {
-            'Meta': {'object_name': 'Election'},
-            'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}),
-            'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
-            'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}),
-            'description': ('django.db.models.fields.TextField', [], {}),
-            'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}),
-            'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
-            'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
-        },
-        'helios.electionlog': {
-            'Meta': {'object_name': 'ElectionLog'},
-            'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'log': ('django.db.models.fields.CharField', [], {'max_length': '500'})
-        },
-        'helios.trustee': {
-            'Meta': {'object_name': 'Trustee'},
-            'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
-            'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.voter': {
-            'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'},
-            'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
-        },
-        'helios.voterfile': {
-            'Meta': {'object_name': 'VoterFile'},
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
-            'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'})
-        }
-    }
-
-    complete_apps = ['helios']
diff --git a/helios/south_migrations/0007_auto__add_field_voterfile_voter_file_content__chg_field_voterfile_vote.py b/helios/south_migrations/0007_auto__add_field_voterfile_voter_file_content__chg_field_voterfile_vote.py
deleted file mode 100644
index 04a7f00..0000000
--- a/helios/south_migrations/0007_auto__add_field_voterfile_voter_file_content__chg_field_voterfile_vote.py
+++ /dev/null
@@ -1,149 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding field 'VoterFile.voter_file_content'
-        db.add_column('helios_voterfile', 'voter_file_content', self.gf('django.db.models.fields.TextField')(null=True), keep_default=False)
-
-        # Changing field 'VoterFile.voter_file'
-        db.alter_column('helios_voterfile', 'voter_file', self.gf('django.db.models.fields.files.FileField')(max_length=250, null=True))
-
-
-    def backwards(self, orm):
-        
-        # Deleting field 'VoterFile.voter_file_content'
-        db.delete_column('helios_voterfile', 'voter_file_content')
-
-        # User chose to not deal with backwards NULL issues for 'VoterFile.voter_file'
-        raise RuntimeError("Cannot reverse this migration. 'VoterFile.voter_file' and its values cannot be restored.")
-
-
-    models = {
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.auditedballot': {
-            'Meta': {'object_name': 'AuditedBallot'},
-            'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'raw_vote': ('django.db.models.fields.TextField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.castvote': {
-            'Meta': {'object_name': 'CastVote'},
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}),
-            'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"})
-        },
-        'helios.election': {
-            'Meta': {'object_name': 'Election'},
-            'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}),
-            'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
-            'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}),
-            'description': ('django.db.models.fields.TextField', [], {}),
-            'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}),
-            'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
-            'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
-        },
-        'helios.electionlog': {
-            'Meta': {'object_name': 'ElectionLog'},
-            'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'log': ('django.db.models.fields.CharField', [], {'max_length': '500'})
-        },
-        'helios.trustee': {
-            'Meta': {'object_name': 'Trustee'},
-            'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
-            'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.voter': {
-            'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'},
-            'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
-        },
-        'helios.voterfile': {
-            'Meta': {'object_name': 'VoterFile'},
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
-            'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_file_content': ('django.db.models.fields.TextField', [], {'null': 'True'})
-        }
-    }
-
-    complete_apps = ['helios']
diff --git a/helios/south_migrations/0008_auto__add_unique_trustee_election_email.py b/helios/south_migrations/0008_auto__add_unique_trustee_election_email.py
deleted file mode 100644
index 974dfd2..0000000
--- a/helios/south_migrations/0008_auto__add_unique_trustee_election_email.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding unique constraint on 'Trustee', fields ['election', 'email']
-        db.create_unique('helios_trustee', ['election_id', 'email'])
-
-
-    def backwards(self, orm):
-        
-        # Removing unique constraint on 'Trustee', fields ['election', 'email']
-        db.delete_unique('helios_trustee', ['election_id', 'email'])
-
-
-    models = {
-        'helios.auditedballot': {
-            'Meta': {'object_name': 'AuditedBallot'},
-            'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'raw_vote': ('django.db.models.fields.TextField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.castvote': {
-            'Meta': {'object_name': 'CastVote'},
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}),
-            'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"})
-        },
-        'helios.election': {
-            'Meta': {'object_name': 'Election'},
-            'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}),
-            'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
-            'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}),
-            'description': ('django.db.models.fields.TextField', [], {}),
-            'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}),
-            'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
-            'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
-        },
-        'helios.electionlog': {
-            'Meta': {'object_name': 'ElectionLog'},
-            'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'log': ('django.db.models.fields.CharField', [], {'max_length': '500'})
-        },
-        'helios.trustee': {
-            'Meta': {'unique_together': "(('election', 'email'),)", 'object_name': 'Trustee'},
-            'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
-            'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.voter': {
-            'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'},
-            'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
-        },
-        'helios.voterfile': {
-            'Meta': {'object_name': 'VoterFile'},
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
-            'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_file_content': ('django.db.models.fields.TextField', [], {'null': 'True'})
-        },
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        }
-    }
-
-    complete_apps = ['helios']
diff --git a/helios/south_migrations/0009_auto__add_field_election_help_email.py b/helios/south_migrations/0009_auto__add_field_election_help_email.py
deleted file mode 100644
index 1500f0e..0000000
--- a/helios/south_migrations/0009_auto__add_field_election_help_email.py
+++ /dev/null
@@ -1,144 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding field 'Election.help_email'
-        db.add_column('helios_election', 'help_email', self.gf('django.db.models.fields.EmailField')(max_length=75, null=True), keep_default=False)
-
-
-    def backwards(self, orm):
-        
-        # Deleting field 'Election.help_email'
-        db.delete_column('helios_election', 'help_email')
-
-
-    models = {
-        'helios.auditedballot': {
-            'Meta': {'object_name': 'AuditedBallot'},
-            'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'raw_vote': ('django.db.models.fields.TextField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.castvote': {
-            'Meta': {'object_name': 'CastVote'},
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}),
-            'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"})
-        },
-        'helios.election': {
-            'Meta': {'object_name': 'Election'},
-            'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}),
-            'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
-            'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}),
-            'description': ('django.db.models.fields.TextField', [], {}),
-            'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}),
-            'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'help_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
-            'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
-        },
-        'helios.electionlog': {
-            'Meta': {'object_name': 'ElectionLog'},
-            'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'log': ('django.db.models.fields.CharField', [], {'max_length': '500'})
-        },
-        'helios.trustee': {
-            'Meta': {'unique_together': "(('election', 'email'),)", 'object_name': 'Trustee'},
-            'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
-            'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.voter': {
-            'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'},
-            'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
-        },
-        'helios.voterfile': {
-            'Meta': {'object_name': 'VoterFile'},
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
-            'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_file_content': ('django.db.models.fields.TextField', [], {'null': 'True'})
-        },
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        }
-    }
-
-    complete_apps = ['helios']
diff --git a/helios/south_migrations/0010_auto__add_field_election_randomize_answer_order.py b/helios/south_migrations/0010_auto__add_field_election_randomize_answer_order.py
deleted file mode 100644
index 96d1eb0..0000000
--- a/helios/south_migrations/0010_auto__add_field_election_randomize_answer_order.py
+++ /dev/null
@@ -1,146 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        # Adding field 'Election.randomize_answer_order'
-        db.add_column('helios_election', 'randomize_answer_order',
-                      self.gf('django.db.models.fields.BooleanField')(default=False),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Election.randomize_answer_order'
-        db.delete_column('helios_election', 'randomize_answer_order')
-
-
-    models = {
-        'helios.auditedballot': {
-            'Meta': {'object_name': 'AuditedBallot'},
-            'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'raw_vote': ('django.db.models.fields.TextField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.castvote': {
-            'Meta': {'object_name': 'CastVote'},
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}),
-            'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"})
-        },
-        'helios.election': {
-            'Meta': {'object_name': 'Election'},
-            'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}),
-            'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
-            'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}),
-            'description': ('django.db.models.fields.TextField', [], {}),
-            'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}),
-            'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'help_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
-            'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'randomize_answer_order': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
-        },
-        'helios.electionlog': {
-            'Meta': {'object_name': 'ElectionLog'},
-            'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'log': ('django.db.models.fields.CharField', [], {'max_length': '500'})
-        },
-        'helios.trustee': {
-            'Meta': {'unique_together': "(('election', 'email'),)", 'object_name': 'Trustee'},
-            'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
-            'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.voter': {
-            'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'},
-            'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
-        },
-        'helios.voterfile': {
-            'Meta': {'object_name': 'VoterFile'},
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
-            'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_file_content': ('django.db.models.fields.TextField', [], {'null': 'True'})
-        },
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        }
-    }
-
-    complete_apps = ['helios']
\ No newline at end of file
diff --git a/helios/south_migrations/0011_auto__add_field_election_election_info_url.py b/helios/south_migrations/0011_auto__add_field_election_election_info_url.py
deleted file mode 100644
index 48c98fb..0000000
--- a/helios/south_migrations/0011_auto__add_field_election_election_info_url.py
+++ /dev/null
@@ -1,147 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        # Adding field 'Election.election_info_url'
-        db.add_column('helios_election', 'election_info_url',
-                      self.gf('django.db.models.fields.CharField')(max_length=300, null=True),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Election.election_info_url'
-        db.delete_column('helios_election', 'election_info_url')
-
-
-    models = {
-        'helios.auditedballot': {
-            'Meta': {'object_name': 'AuditedBallot'},
-            'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'raw_vote': ('django.db.models.fields.TextField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.castvote': {
-            'Meta': {'object_name': 'CastVote'},
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}),
-            'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"})
-        },
-        'helios.election': {
-            'Meta': {'object_name': 'Election'},
-            'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}),
-            'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
-            'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}),
-            'description': ('django.db.models.fields.TextField', [], {}),
-            'election_info_url': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'}),
-            'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}),
-            'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'help_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
-            'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'randomize_answer_order': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
-        },
-        'helios.electionlog': {
-            'Meta': {'object_name': 'ElectionLog'},
-            'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'log': ('django.db.models.fields.CharField', [], {'max_length': '500'})
-        },
-        'helios.trustee': {
-            'Meta': {'unique_together': "(('election', 'email'),)", 'object_name': 'Trustee'},
-            'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
-            'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.voter': {
-            'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'},
-            'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
-        },
-        'helios.voterfile': {
-            'Meta': {'object_name': 'VoterFile'},
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
-            'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_file_content': ('django.db.models.fields.TextField', [], {'null': 'True'})
-        },
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        }
-    }
-
-    complete_apps = ['helios']
\ No newline at end of file
diff --git a/helios/south_migrations/0012_auto__add_field_election_result_released_at.py b/helios/south_migrations/0012_auto__add_field_election_result_released_at.py
deleted file mode 100644
index 0cc6853..0000000
--- a/helios/south_migrations/0012_auto__add_field_election_result_released_at.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        # Adding field 'Election.result_released_at'
-        db.add_column('helios_election', 'result_released_at',
-                      self.gf('django.db.models.fields.DateTimeField')(default=None, null=True),
-                      keep_default=False)
-
-
-    def backwards(self, orm):
-        # Deleting field 'Election.result_released_at'
-        db.delete_column('helios_election', 'result_released_at')
-
-
-    models = {
-        'helios.auditedballot': {
-            'Meta': {'object_name': 'AuditedBallot'},
-            'added_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'raw_vote': ('django.db.models.fields.TextField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'})
-        },
-        'helios.castvote': {
-            'Meta': {'object_name': 'CastVote'},
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'invalidated_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'quarantined_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'released_from_quarantine_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'verified_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'vote_tinyhash': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True'}),
-            'voter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Voter']"})
-        },
-        'helios.election': {
-            'Meta': {'object_name': 'Election'},
-            'admin': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']"}),
-            'archived_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'cast_url': ('django.db.models.fields.CharField', [], {'max_length': '500'}),
-            'complaint_period_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'datatype': ('django.db.models.fields.CharField', [], {'default': "'legacy/Election'", 'max_length': '250'}),
-            'description': ('django.db.models.fields.TextField', [], {}),
-            'election_info_url': ('django.db.models.fields.CharField', [], {'max_length': '300', 'null': 'True'}),
-            'election_type': ('django.db.models.fields.CharField', [], {'default': "'election'", 'max_length': '250'}),
-            'eligibility': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'encrypted_tally': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'featured_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'frozen_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'help_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
-            'openreg': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'private_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'private_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'questions': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'randomize_answer_order': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'registration_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'result': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'result_proof': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'result_released_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'short_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'tallies_combined_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_finished_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'tallying_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'use_advanced_audit_features': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
-            'use_voter_aliases': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'voters_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voting_ended_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_ends_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_extended_until': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_started_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
-            'voting_starts_at': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'})
-        },
-        'helios.electionlog': {
-            'Meta': {'object_name': 'ElectionLog'},
-            'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'log': ('django.db.models.fields.CharField', [], {'max_length': '500'})
-        },
-        'helios.trustee': {
-            'Meta': {'unique_together': "(('election', 'email'),)", 'object_name': 'Trustee'},
-            'decryption_factors': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'decryption_proofs': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
-            'pok': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'public_key_hash': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'secret_key': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        'helios.voter': {
-            'Meta': {'unique_together': "(('election', 'voter_login_id'),)", 'object_name': 'Voter'},
-            'alias': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'cast_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios_auth.User']", 'null': 'True'}),
-            'uuid': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
-            'vote': ('helios.datatypes.djangofield.LDObjectField', [], {'null': 'True'}),
-            'vote_hash': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_email': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_login_id': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}),
-            'voter_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'voter_password': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
-        },
-        'helios.voterfile': {
-            'Meta': {'object_name': 'VoterFile'},
-            'election': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helios.Election']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'num_voters': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
-            'processing_finished_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'processing_started_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
-            'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'voter_file': ('django.db.models.fields.files.FileField', [], {'max_length': '250', 'null': 'True'}),
-            'voter_file_content': ('django.db.models.fields.TextField', [], {'null': 'True'})
-        },
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        }
-    }
-
-    complete_apps = ['helios']
\ No newline at end of file
diff --git a/helios/south_migrations/__init__.py b/helios/south_migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/helios/stats_url_names.py b/helios/stats_url_names.py
new file mode 100644
index 0000000..2b8ffe2
--- /dev/null
+++ b/helios/stats_url_names.py
@@ -0,0 +1,5 @@
+STATS_HOME="stats@home"
+STATS_FORCE_QUEUE="stats@force-queue"
+STATS_ELECTIONS="stats@elections"
+STATS_ELECTIONS_PROBLEMS="stats@elections-problems"
+STATS_RECENT_VOTES="stats@recent-votes"
diff --git a/helios/stats_urls.py b/helios/stats_urls.py
index 2d26d19..ac3b143 100644
--- a/helios/stats_urls.py
+++ b/helios/stats_urls.py
@@ -4,15 +4,15 @@ Helios URLs for Election related stuff
 Ben Adida (ben@adida.net)
 """
 
-from django.conf.urls import *
+from django.conf.urls import url
 
-from helios.stats_views import *
+from helios.stats_views import (home, force_queue, elections, recent_problem_elections, recent_votes)
+import helios.stats_url_names as names
 
-urlpatterns = patterns(
-    '',
-    (r'^$', home),
-    (r'^force-queue$', force_queue),
-    (r'^elections$', elections),
-    (r'^problem-elections$', recent_problem_elections),
-    (r'^recent-votes$', recent_votes),
-)
+urlpatterns = [
+    url(r'^$', home, name=names.STATS_HOME),
+    url(r'^force-queue$', force_queue, name=names.STATS_FORCE_QUEUE),
+    url(r'^elections$', elections, name=names.STATS_ELECTIONS),
+    url(r'^problem-elections$', recent_problem_elections, name=names.STATS_ELECTIONS_PROBLEMS),
+    url(r'^recent-votes$', recent_votes, name=names.STATS_RECENT_VOTES),
+]
diff --git a/helios/stats_views.py b/helios/stats_views.py
index 7d2a62e..5e71a32 100644
--- a/helios/stats_views.py
+++ b/helios/stats_views.py
@@ -2,18 +2,19 @@
 Helios stats views
 """
 
-from django.core.urlresolvers import reverse
-from django.core.mail import send_mail
+import datetime
+
 from django.core.paginator import Paginator
-from django.http import *
-from django.db import transaction
-from django.db.models import *
+from django.urls import reverse
+from django.db.models import Max, Count
+from django.http import HttpResponseRedirect
 
-from security import *
-from helios_auth.security import get_user, save_in_session_across_logouts
-from view_utils import *
+from helios import tasks, url_names
+from helios.models import CastVote, Election
+from helios_auth.security import get_user
+from security import PermissionDenied
+from view_utils import render_template
 
-from helios import tasks
 
 def require_admin(request):
   user = get_user(request)
@@ -33,7 +34,7 @@ def force_queue(request):
   for cv in votes_in_queue:
     tasks.cast_vote_verify_and_store.delay(cv.id)
 
-  return HttpResponseRedirect(reverse(home))
+  return HttpResponseRedirect(reverse(url_names.stats.STATS_HOME))
 
 def elections(request):
   user = require_admin(request)
@@ -42,8 +43,7 @@ def elections(request):
   limit = int(request.GET.get('limit', 25))
   q = request.GET.get('q','')
 
-  elections = Election.objects.filter(name__icontains = q)
-  elections.all().order_by('-created_at')
+  elections = Election.objects.filter(name__icontains = q).order_by('-created_at')
   elections_paginator = Paginator(elections, limit)
   elections_page = elections_paginator.page(page)
 
diff --git a/helios/tasks.py b/helios/tasks.py
index 5f7bba8..8610097 100644
--- a/helios/tasks.py
+++ b/helios/tasks.py
@@ -4,20 +4,18 @@ Celery queued tasks for Helios
 2010-08-01
 ben@adida.net
 """
+import copy
+from celery import shared_task
+from celery.utils.log import get_logger
 
-from celery.decorators import task
-
-from models import *
-from view_utils import render_template_raw
 import signals
+from models import CastVote, Election, Voter, VoterFile
+from view_utils import render_template_raw
 
-import copy
-
-from django.conf import settings
 
-@task()
+@shared_task
 def cast_vote_verify_and_store(cast_vote_id, status_update_message=None, **kwargs):
-    cast_vote = CastVote.objects.get(id = cast_vote_id)
+    cast_vote = CastVote.objects.get(id=cast_vote_id)
     result = cast_vote.verify_and_store()
 
     voter = cast_vote.voter
@@ -27,23 +25,22 @@ def cast_vote_verify_and_store(cast_vote_id, status_update_message=None, **kwarg
     if result:
         # send the signal
         signals.vote_cast.send(sender=election, election=election, user=user, voter=voter, cast_vote=cast_vote)
-        
-        if status_update_message and user.can_update_status():
-            from views import get_election_url
 
+        if status_update_message and user.can_update_status():
             user.update_status(status_update_message)
     else:
-        logger = cast_vote_verify_and_store.get_logger(**kwargs)
+        logger = get_logger(cast_vote_verify_and_store.__name__)
         logger.error("Failed to verify and store %d" % cast_vote_id)
-    
-@task()
+
+
+@shared_task
 def voters_email(election_id, subject_template, body_template, extra_vars={},
                  voter_constraints_include=None, voter_constraints_exclude=None):
     """
     voter_constraints_include are conditions on including voters
     voter_constraints_exclude are conditions on excluding voters
     """
-    election = Election.objects.get(id = election_id)
+    election = Election.objects.get(id=election_id)
 
     # select the right list of voters
     voters = election.voter_set.all()
@@ -53,61 +50,66 @@ def voters_email(election_id, subject_template, body_template, extra_vars={},
         voters = voters.exclude(**voter_constraints_exclude)
 
     for voter in voters:
-        single_voter_email.delay(voter.uuid, subject_template, body_template, extra_vars)            
+        single_voter_email.delay(voter.uuid, subject_template, body_template, extra_vars)
+
 
-@task()
+@shared_task
 def voters_notify(election_id, notification_template, extra_vars={}):
-    election = Election.objects.get(id = election_id)
+    election = Election.objects.get(id=election_id)
     for voter in election.voter_set.all():
         single_voter_notify.delay(voter.uuid, notification_template, extra_vars)
 
-@task()
+
+@shared_task
 def single_voter_email(voter_uuid, subject_template, body_template, extra_vars={}):
-    voter = Voter.objects.get(uuid = voter_uuid)
+    voter = Voter.objects.get(uuid=voter_uuid)
 
     the_vars = copy.copy(extra_vars)
-    the_vars.update({'voter' : voter})
+    the_vars.update({'voter': voter})
 
     subject = render_template_raw(None, subject_template, the_vars)
     body = render_template_raw(None, body_template, the_vars)
 
     voter.send_message(subject, body)
 
-@task()
+
+@shared_task
 def single_voter_notify(voter_uuid, notification_template, extra_vars={}):
-    voter = Voter.objects.get(uuid = voter_uuid)
+    voter = Voter.objects.get(uuid=voter_uuid)
 
     the_vars = copy.copy(extra_vars)
-    the_vars.update({'voter' : voter})
+    the_vars.update({'voter': voter})
 
     notification = render_template_raw(None, notification_template, the_vars)
 
     voter.send_notification(notification)
 
-@task()
+
+@shared_task
 def election_compute_tally(election_id):
-    election = Election.objects.get(id = election_id)
+    election = Election.objects.get(id=election_id)
     election.compute_tally()
 
-    election_notify_admin.delay(election_id = election_id,
-                                subject = "encrypted tally computed",
-                                body = """
+    election_notify_admin.delay(election_id=election_id,
+                                subject="encrypted tally computed",
+                                body="""
 The encrypted tally for election %s has been computed.
 
 --
 Helios
 """ % election.name)
-                                
+
     if election.has_helios_trustee():
-        tally_helios_decrypt.delay(election_id = election.id)
+        tally_helios_decrypt.delay(election_id=election.id)
+
 
-@task()
+@shared_task
 def tally_helios_decrypt(election_id):
-    election = Election.objects.get(id = election_id)
+    election = Election.objects.get(id=election_id)
     election.helios_trustee_decrypt()
-    election_notify_admin.delay(election_id = election_id,
-                                subject = 'Helios Decrypt',
-                                body = """
+    election_notify_admin.delay(election_id=election_id,
+                                subject='Helios Decrypt',
+                                body="""
 Helios has decrypted its portion of the tally
 for election %s.
 
@@ -115,13 +117,14 @@ for election %s.
 Helios
 """ % election.name)
 
-@task()
+
+@shared_task
 def voter_file_process(voter_file_id):
-    voter_file = VoterFile.objects.get(id = voter_file_id)
+    voter_file = VoterFile.objects.get(id=voter_file_id)
     voter_file.process()
-    election_notify_admin.delay(election_id = voter_file.election.id, 
-                                subject = 'voter file processed',
-                                body = """
+    election_notify_admin.delay(election_id=voter_file.election.id,
+                                subject='voter file processed',
+                                body="""
 Your voter file upload for election %s
 has been processed.
 
@@ -131,7 +134,8 @@ has been processed.
 Helios
 """ % (voter_file.election.name, voter_file.num_voters))
 
-@task()
+
+@shared_task
 def election_notify_admin(election_id, subject, body):
-    election = Election.objects.get(id = election_id)
+    election = Election.objects.get(id=election_id)
     election.admin.send_message(subject, body)
diff --git a/helios/templates/_castconfirm_docast.html b/helios/templates/_castconfirm_docast.html
index fa6b3e7..1a91b21 100644
--- a/helios/templates/_castconfirm_docast.html
+++ b/helios/templates/_castconfirm_docast.html
@@ -23,7 +23,7 @@ You are logged in as <u>{{voter.display_html_big|safe}}</u><br /><br />
 </form>
 
 <p>
-    <button class="tiny" onclick="document.location='./view';">cancel</button><br />
+    <button class="tiny" onclick="document.location='{% url "election@view" election.uuid %}';">cancel</button><br />
     <span style="font-size:0.8em;">If you cancel now, your ballot will <em>NOT</em> be recorded.<br />
     You can start the voting process over again, of course.</span>
 </p>
diff --git a/helios/templates/_castconfirm_password.html b/helios/templates/_castconfirm_password.html
index 25e31de..03e698a 100644
--- a/helios/templates/_castconfirm_password.html
+++ b/helios/templates/_castconfirm_password.html
@@ -1,5 +1,5 @@
 Please provide the voter ID and password you received by email.<br /><br />
-<form method="post" action="{% url "helios.views.password_voter_login" election.uuid %}" onsubmit="show_waiting()">
+<form method="post" action="{% url "election@password-voter-login" election.uuid %}" onsubmit="show_waiting()">
 <input type="hidden" name="csrf_token" value="{{csrf_token}}" />
 <input type="hidden" name="return_url" value="{{return_url}}" />
 <input type="hidden" name="cast_ballot" value="{{cast_ballot}}" />
diff --git a/helios/templates/cast_done.html b/helios/templates/cast_done.html
index 75265ab..dc40805 100644
--- a/helios/templates/cast_done.html
+++ b/helios/templates/cast_done.html
@@ -15,12 +15,12 @@
 
 {% if logout %}
 <p><b>For your safety, we have logged you out.</b></p>
-<iframe width="0" height="0" border="0" frameborder="0" src="/auth/logout">
+<iframe width="0" height="0" border="0" frameborder="0" src="{% url "auth@logout" %}">
 </iframe>
 {% endif %}
 
 <p style="font-size: 1.4em;">
-[ <a href="{% url "helios.views.one_election_view" election.uuid %}">return to election info</a> ]
+[ <a href="{% url "election@view" election.uuid %}">return to election info</a> ]
 </p>
 
 {% endblock %}
diff --git a/helios/templates/castvote.html b/helios/templates/castvote.html
index 1c21d73..21b5fa7 100644
--- a/helios/templates/castvote.html
+++ b/helios/templates/castvote.html
@@ -3,7 +3,7 @@
 {% block title %}{{cast_vote.vote_tinyhash}} &mdash; {{election.name}}{% endblock %}
 {% block content %}
 <h2 class="title">Cast Vote {{cast_vote.vote_tinyhash}}</h2>
-cast in <a href="{% url "helios.views.one_election_view" election.uuid %}">{{election.name}}</a><br />
+cast in <a href="{% url "election@view" election.uuid %}">{{election.name}}</a><br />
 Fingerprint: <tt>{{cast_vote.vote_hash}}</tt><br />
 by <b><u>
 {% if the_voter.alias %}
diff --git a/helios/templates/combine_decryptions.html b/helios/templates/combine_decryptions.html
index 3530867..89d60f1 100644
--- a/helios/templates/combine_decryptions.html
+++ b/helios/templates/combine_decryptions.html
@@ -2,7 +2,7 @@
 
 {% block title %}Compute Tally &mdash; {{election.name}}{% endblock %}
 {% block content %}
-  <h2 class="title">{{election.name}} &mdash; Compute Tally <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">cancel</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Compute Tally <span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">cancel</a>]</span></h2>
 
   <p>
     You are about to compute the tally for this election. You only will then see the results.
diff --git a/helios/templates/election_audited_ballots.html b/helios/templates/election_audited_ballots.html
index 2e01f9b..8225c1b 100644
--- a/helios/templates/election_audited_ballots.html
+++ b/helios/templates/election_audited_ballots.html
@@ -3,7 +3,7 @@
 {% block title %}Audited Ballots for {{election.name}}{% endblock %}
 
 {% block content %}
-  <h2 class="title">{{election.name}} &mdash; Audited Ballots <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election_uuid=election.uuid %}">back to election</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Audited Ballots <span style="font-size:0.7em;">[<a href="{% url "election@view" election_uuid=election.uuid %}">back to election</a>]</span></h2>
 
 <p>
 When you prepare a ballot with Helios, you immediately receive a smart ballot tracker. Before you choose to cast that ballot, you have the option to ask Helios to "break open" that encrypted ballot and verify that Helios encrypted your ballot correctly. Once that's done, you can post that opened ballot here, on the audited ballots' list, for everyone to verify (your identity is not included). Once you've done this, you have to re-encrypt your choices and obtain a different smart ballot tracker. This helps reduce the chance that someone might coerce you to vote differently from your true choice.
@@ -14,7 +14,7 @@ These ballots are <em>not cast</em>, and they will not be counted. They are just
 </p>
 
 <p>
-  To verify an audited ballot, copy its entire content and paste it in the <a target="_new" href="/booth/single-ballot-verify.html?election_url={% url "helios.views.one_election" election.uuid %}">single ballot verifier</a>.
+  To verify an audited ballot, copy its entire content and paste it in the <a target="_new" href="/booth/single-ballot-verify.html?election_url={% url "election@home" election.uuid %}">single ballot verifier</a>.
 </p>
 
 {% if audited_ballots %}
diff --git a/helios/templates/election_bboard.html b/helios/templates/election_bboard.html
index a5fd7e7..2582193 100644
--- a/helios/templates/election_bboard.html
+++ b/helios/templates/election_bboard.html
@@ -3,7 +3,7 @@
 {% block title %}Ballot Tracking Center for {{election.name}}{% endblock %}
 
 {% block content %}
-  <h2 class="title">{{election.name}} &mdash; Ballot Tracking Center <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election_uuid=election.uuid %}">back to election</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Ballot Tracking Center <span style="font-size:0.7em;">[<a href="{% url "election@view" election_uuid=election.uuid %}">back to election</a>]</span></h2>
 
 <p>
   This is the ballot tracking center, which displays the tracking numbers of all cast ballots in this election.
@@ -16,15 +16,15 @@
 Voters {{offset_plus_one}} - {{offset_plus_limit}} &nbsp;&nbsp;
 
 {% if next_after %}
-<a href="./bboard?after={{next_after}}&offset={{offset_plus_limit}}">next {{limit}}</a> &nbsp;&nbsp;
+<a href="{% url "election@bboard" election.uuid %}?after={{next_after}}&offset={{offset_plus_limit}}">next {{limit}}</a> &nbsp;&nbsp;
 {% endif %}
 
 {% ifequal offset 0 %}
 {% else %}
-<a href="./bboard">back to start</a> &nbsp;&nbsp;
+<a href="{% url "election@bboard" election.uuid %}">back to start</a> &nbsp;&nbsp;
 {% endifequal %}
 {% if more_p %}
-<a href="./bboard?after={{next_after}}&offset={{next_offset}}">next {{limit}}</a>
+<a href="{% url "election@bboard" election.uuid %}?after={{next_after}}&offset={{next_offset}}">next {{limit}}</a>
 {% endif %}
 <table class="pretty">
 <tr><th>
@@ -40,7 +40,7 @@ Name
 {{voter.alias}}
 {% else %}
 <img border="0" height="20" src="/static/auth/login-icons/{{voter.voter_type}}.png" alt="{{voter.voter_type}}" /> {% if voter.name %}{{voter.name}}{% else %}{{voter.voter_id}}{% endif %}
-{% endif %}</td><td><tt style="font-size: 1.4em;;">{% if voter.vote_hash %}{{voter.vote_hash}} <span style="font-size:0.8em;">[<a href="{% url "helios.views.voter_last_vote" election_uuid=election.uuid voter_uuid=voter.uuid %}">view</a>]</span>{% else %}&mdash;{% endif %}</tt></td></tr>
+{% endif %}</td><td><tt style="font-size: 1.4em;;">{% if voter.vote_hash %}{{voter.vote_hash}} <span style="font-size:0.8em;">[<a href="{% url "election@ballots@voter@last" election_uuid=election.uuid voter_uuid=voter.uuid %}">view</a>]</span>{% else %}&mdash;{% endif %}</tt></td></tr>
 {% endfor %}
 </table>
 
diff --git a/helios/templates/election_build.html b/helios/templates/election_build.html
index 4526d6a..8f56185 100644
--- a/helios/templates/election_build.html
+++ b/helios/templates/election_build.html
@@ -1,7 +1,7 @@
 {% extends "helios/templates/cryptobase.html" %}
 
 {% block content %}
-  <h2 class="title">{{election.name}} &mdash; Questions <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">back to election</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Questions <span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">back to election</a>]</span></h2>
 
   <script language="javascript">
 {% if election.questions %}
diff --git a/helios/templates/election_cast_confirm.html b/helios/templates/election_cast_confirm.html
index 346284c..173d729 100644
--- a/helios/templates/election_cast_confirm.html
+++ b/helios/templates/election_cast_confirm.html
@@ -73,7 +73,7 @@ requires election-specific credentials.
   {% endif %}
   </b><br /></p>
 <p>
-    [<a href="{% url "helios.views.one_election_view" election.uuid %}">return to the main election page</a>]
+    [<a href="{% url "election@view" election.uuid %}">return to the main election page</a>]
 </p>
 {% else %}
 <p>
diff --git a/helios/templates/election_compute_tally.html b/helios/templates/election_compute_tally.html
index fd98440..2ad050a 100644
--- a/helios/templates/election_compute_tally.html
+++ b/helios/templates/election_compute_tally.html
@@ -19,12 +19,12 @@
 <input type="hidden" name="csrf_token" value="{{csrf_token}}" />
     
 <input class="button" type="submit" value="compute encrypted tally!" />
-<button onclick="document.location='./view'; return false;">never mind</button>
+<button onclick="document.location='{% url "election@view" election.uuid %}'; return false;">never mind</button>
 </form>
 {% else %}
 <p>
 No votes have been cast in this election. At least one vote must be cast before you compute the tally.<br /><br />
-<a href="./view">back to election</a>
+<a href="{% url "election@view" election.uuid %}">back to election</a>
 </p>
 {% endif %}
 </div>
diff --git a/helios/templates/election_edit.html b/helios/templates/election_edit.html
index 2519fb7..025b258 100644
--- a/helios/templates/election_edit.html
+++ b/helios/templates/election_edit.html
@@ -2,7 +2,7 @@
 
 {% block content %}
 
-  <h2 class="title">{{election.name}} &mdash; Update <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">cancel</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Update <span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">cancel</a>]</span></h2>
   
 {% if error %}
 <p style="color: red;">
diff --git a/helios/templates/election_extend.html b/helios/templates/election_extend.html
index 0db2c44..813afe7 100644
--- a/helios/templates/election_extend.html
+++ b/helios/templates/election_extend.html
@@ -2,7 +2,7 @@
 
 {% block content %}
 
-  <h2 class="title">{{election.name}} &mdash; Extend Voting <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">cancel</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Extend Voting <span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">cancel</a>]</span></h2>
   
   <form class="prettyform" action="" method="POST" id="edit_election_form">
     <input type="hidden" name="csrf_token" value="{{csrf_token}}" />
diff --git a/helios/templates/election_freeze.html b/helios/templates/election_freeze.html
index a0aad4d..f0229be 100644
--- a/helios/templates/election_freeze.html
+++ b/helios/templates/election_freeze.html
@@ -29,14 +29,14 @@ You must freeze the ballot before you can contact voters.
         <li>{{issue.action}}</li>
         {% endfor %}
     </ul>
-    <a href="{% url "helios.views.one_election_view" election.uuid %}">go back to the election</a>
+    <a href="{% url "election@view" election.uuid %}">go back to the election</a>
 </p>
 {% else %}
 <form method="post" action="">
 <input type="hidden" name="csrf_token" value="{{csrf_token}}" />
     
 <input class="button" type="submit" value="Freeze the ballot" />
-<button onclick="document.location='./view'; return false;">never mind</button>
+<button onclick="document.location='{% url "election@view" election.uuid %}'; return false;">never mind</button>
 </form>
 {% endif %}
 
diff --git a/helios/templates/election_keygenerator.html b/helios/templates/election_keygenerator.html
index 4521fc9..bce34b8 100644
--- a/helios/templates/election_keygenerator.html
+++ b/helios/templates/election_keygenerator.html
@@ -35,7 +35,7 @@ $(document).ready(function() {
     $('#generator').hide();
 
     // get some more server-side randomness for keygen
-    $.getJSON('../../get-randomness', function(result) {
+    $.getJSON('{% url "election@get-randomness" election.uuid %}', function(result) {
        sjcl.random.addEntropy(result.randomness);
        BigInt.setup(function() {
           ELGAMAL_PARAMS = ElGamal.Params.fromJSONObject({{eg_params_json|safe}});
@@ -159,7 +159,7 @@ Your key has been generated, but you may choose to<br /><a href="javascript:clea
 </p>
 </div>
 
-<form method="post" id="pk_form" action="{% url "helios.views.trustee_upload_pk" election.uuid trustee.uuid %}">
+<form method="post" id="pk_form" action="{% url "election@trustee@upload-pk" election.uuid trustee.uuid %}">
 <h3>Your Public Key</h3>
 <p>
     It's time to upload the public key to the server.
diff --git a/helios/templates/election_not_started.html b/helios/templates/election_not_started.html
index 4ba944e..5fba613 100644
--- a/helios/templates/election_not_started.html
+++ b/helios/templates/election_not_started.html
@@ -9,6 +9,6 @@
   </p>
   
   <p>
-      <a href="{% url "helios.views.one_election_view" election.uuid %}">back to the election</a>
+      <a href="{% url "election@view" election.uuid %}">back to the election</a>
   </p>
 {% endblock %}
diff --git a/helios/templates/election_questions.html b/helios/templates/election_questions.html
index d7b8f73..468c65e 100644
--- a/helios/templates/election_questions.html
+++ b/helios/templates/election_questions.html
@@ -2,7 +2,7 @@
 
 {% block title %}Questions for {{election.name}}{% endblock %}
 {% block content %}
-  <h3 class="title">{{election.name}} &mdash; Questions <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">back to election</a>]</span></h3>
+  <h3 class="title">{{election.name}} &mdash; Questions <span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">back to election</a>]</span></h3>
 
   <script language="javascript">
 {% if election.questions %}
diff --git a/helios/templates/election_register.html b/helios/templates/election_register.html
index e1c731e..96a6004 100644
--- a/helios/templates/election_register.html
+++ b/helios/templates/election_register.html
@@ -16,7 +16,7 @@
     You are <em>not</em> registered for this election.
 </p>
 
-<form method="post" action="{% url "helios.views.one_election_register" election.uuid %}">
+<form method="post" action="{% url "election@register" election.uuid %}">
 <input type="submit" value="register!" />
 </form>
 {% endif %}
diff --git a/helios/templates/election_tallied.html b/helios/templates/election_tallied.html
index 9b00b63..b711682 100644
--- a/helios/templates/election_tallied.html
+++ b/helios/templates/election_tallied.html
@@ -9,6 +9,6 @@
   </p>
   
   <p>
-      <a href="{% url "helios.views.one_election_view" election.uuid %}">view the election tally</a>
+      <a href="{% url "election@view" election.uuid %}">view the election tally</a>
   </p>
 {% endblock %}
\ No newline at end of file
diff --git a/helios/templates/election_view.html b/helios/templates/election_view.html
index 5973c20..fcb7121 100644
--- a/helios/templates/election_view.html
+++ b/helios/templates/election_view.html
@@ -5,7 +5,7 @@
   <h3 class="title">{{ election.name }}
 {% if admin_p %}
 {% if not election.frozen_at %}
-<small><a class="small button" href="{% url "helios.views.one_election_edit" election.uuid %}">edit</a></small>
+<small><a class="small button" href="{% url "election@edit" election.uuid %}">edit</a></small>
 {% endif %}
 {% endif %}</h3>
 <p style="padding-top:0px; margin-top:0px">
@@ -14,8 +14,8 @@
 [archived]
 {% endif %}
 {% if admin_p %}
-&nbsp;{% if election.is_archived %}<a class="small button" href="{% url "helios.views.one_election_archive" election_uuid=election.uuid %}?archive_p=0">unarchive it</a>{% else %}<a class="small button" href="{% url "helios.views.one_election_archive" election_uuid=election.uuid %}?archive_p=1">archive it</a>{% endif %}
-<a class="small button" onclick="return window.confirm('Are you sure you want to copy this election?');" href="{% url "helios.views.one_election_copy" election_uuid=election.uuid %}">copy</a>
+&nbsp;{% if election.is_archived %}<a class="small button" href="{% url "election@archive" election_uuid=election.uuid %}?archive_p=0">unarchive it</a>{% else %}<a class="small button" href="{% url "election@archive" election_uuid=election.uuid %}?archive_p=1">archive it</a>{% endif %}
+<a class="small button" onclick="return window.confirm('Are you sure you want to copy this election?');" href="{% url "election@copy" election_uuid=election.uuid %}">copy</a>
 {% endif %}
 <br />
 {% if admin_p %}
@@ -23,12 +23,12 @@
 {% if election.featured_p %}
 this {{election.election_type}} is featured on the front page.
 {% if can_feature_p %}
-[<a href="{% url "helios.views.one_election_set_featured" election.uuid %}?featured_p=0">unfeature it</a>]
+[<a href="{% url "election@set-featured" election.uuid %}?featured_p=0">unfeature it</a>]
 {% endif %}
 {% else %}
 this {{election.election_type}} is <u>not</u> featured on the front page.
 {% if can_feature_p %}
-[<a href="{% url "helios.views.one_election_set_featured" election.uuid %}?featured_p=1">feature it</a>]
+[<a href="{% url "election@set-featured" election.uuid %}?featured_p=1">feature it</a>]
 {% endif %}
 {% endif %}
 {% endif %}
@@ -51,11 +51,11 @@ this {{election.election_type}} is <u>not</u> featured on the front page.
 {% endif %}
 
 <p align="center" style="font-size: 1.5em;">
-<a href="{% url "helios.views.one_election_questions" election.uuid %}">questions ({% if election.questions %}{{election.questions|length}}{% else %}0{% endif %})</a>
+<a href="{% url "election@questions" election.uuid %}">questions ({% if election.questions %}{{election.questions|length}}{% else %}0{% endif %})</a>
 &nbsp;&nbsp;|&nbsp;&nbsp;
-<a href="{% url "helios.views.voters_list_pretty" election.uuid %}">voters &amp; ballots</a>
+<a href="{% url "election@voters@list-pretty" election.uuid %}">voters &amp; ballots</a>
 &nbsp;&nbsp;|&nbsp;&nbsp;
-<a href="{% url "helios.views.list_trustees_view" election.uuid %}">trustees ({{trustees|length}})</a>
+<a href="{% url "election@trustees@view" election.uuid %}">trustees ({{trustees|length}})</a>
 </p>
 
 {% if admin_p %}
@@ -87,7 +87,7 @@ this {{election.election_type}} is <u>not</u> featured on the front page.
 {{issue.action}}{% if forloop.last %}{% else %}, and{% endif %}<br />
 {% endfor %}
 {% else %}
-<a href="{% url "helios.views.one_election_freeze" election.uuid %}">freeze ballot and open election.</a>
+<a href="{% url "election@freeze" election.uuid %}">freeze ballot and open election.</a>
 <br />
 {% if election.voting_starts_at %}
 once you do this, the election will be ready for voting and will open automatically<br />
@@ -104,20 +104,20 @@ once you do this, the election will be immediately open for voting.
 Tally computation is under way.<br />
 Reload this page in a couple of minutes.
 {% else %}
-<a href="{% url "helios.views.one_election_compute_tally" election.uuid %}">compute encrypted tally</a><br />
+<a href="{% url "election@compute-tally" election.uuid %}">compute encrypted tally</a><br />
 The encrypted votes will be combined into an encrypted tally. Once this is done,<br />
 trustees will be asked to provide their share of the decryption.
 {% endif %}
 {% else %}
 
 {% if election.result %}
-<a href="{% url "helios.views.release_result" election.uuid %}">release result</a><br />
+<a href="{% url "election@release-result" election.uuid %}">release result</a><br />
 The result displayed below is visible only to you.<br />
 Once you release the result, it will be visible to everyone.
 {% else %}
 
 {% if election.ready_for_decryption_combination %}
-<a href="{% url "helios.views.combine_decryptions" election.uuid %}">
+<a href="{% url "election@combine-decryptions" election.uuid %}">
 {% if election.num_trustees == 1 %}
 compute results
 {% else %}
@@ -131,7 +131,7 @@ The decryption shares from the trustees will be combined and the tally computed.
 Once you do this, the tally will visible to you, the administrator, only.
 {% endif %}
 {% else %}
-<a href="{% url "helios.views.list_trustees_view" election.uuid %}">trustees (for decryption)</a>
+<a href="{% url "election@trustees@view" election.uuid %}">trustees (for decryption)</a>
 {% endif %}
 
 {% endif %}
@@ -236,7 +236,7 @@ You are <em>not eligible</em> to vote in this {{election.election_type}}.
 {% if election.openreg %}
 {% if election.eligibility %}
 This election is open to: {{election.pretty_eligibility|safe}}
-<a href="{{settings.SECURE_URL_HOST}}{% url "helios_auth.views.index" %}?return_url={{CURRENT_URL}}">Log in</a> to check your eligibility.
+<a href="{{settings.SECURE_URL_HOST}}{% url "auth@index" %}?return_url={{CURRENT_URL}}">Log in</a> to check your eligibility.
 {% else %}
 Anyone can vote in this election.
 {% endif %}
@@ -249,7 +249,7 @@ Anyone can vote in this election.
 {% endif %}
 
 {% if admin_p and election.voting_ends_at and not election.tallying_started_at %}
-<br /><a href="{% url "helios.views.one_election_extend" election.uuid %}">extend voting</a><br />
+<br /><a href="{% url "election@extend" election.uuid %}">extend voting</a><br />
 {% endif %}
 
 <div style="background: lightyellow; padding:5px; padding-left: 10px; margin-top: 15px; border: 1px solid #aaa; width: 720px;" class="round">
@@ -271,22 +271,22 @@ Anyone can vote in this election.
 {% endif %}
 
 <p style="font-size: 1.3em;">
-<a href="{% url "helios.views.voters_list_pretty" election.uuid %}">Ballot Tracking Center</a> &nbsp;| &nbsp;
-<a href="{% url "helios.views.one_election_audited_ballots" election.uuid %}">Audited Ballots</a>
+<a href="{% url "election@voters@list-pretty" election.uuid %}">Ballot Tracking Center</a> &nbsp;| &nbsp;
+<a href="{% url "election@audited-ballots" election.uuid %}">Audited Ballots</a>
 </p>
 
 {% endif %}
 
 {% if not election.voting_has_started %}
 <p style="font-size: 1.2em;">
-  <a href="{{SECURE_URL_HOST}}/booth/vote.html?election_url={% url "helios.views.one_election" election.uuid %}">preview booth</a>
+  <a href="{{SECURE_URL_HOST}}/booth/vote.html?election_url={% url "election@home" election.uuid %}">preview booth</a>
 </p>
 {% endif %}  
 
 {% if election.voting_has_stopped %}
 <p style="font-size: 1.2em;">
 {% if election.result %}
-verify <a target="_blank" href="/verifier/verify.html?election_url={% url "helios.views.one_election" election.uuid %}">election tally</a>.
+verify <a target="_blank" href="/verifier/verify.html?election_url={% url "election@home" election.uuid %}">election tally</a>.
 {% endif %}
 
 review the <a href="{{vote_url}}">voting booth</a>.
diff --git a/helios/templates/elections_administered.html b/helios/templates/elections_administered.html
index 03bc226..563ecc9 100644
--- a/helios/templates/elections_administered.html
+++ b/helios/templates/elections_administered.html
@@ -5,7 +5,7 @@
 
 <ul>
 {% for election in elections %}
-<li> <a href="{% url "helios.views.one_election_view" election.uuid %}">{{election.name}}</a><em> - {{election.num_voters}} voters / {{election.num_cast_votes}} cast votes</em></li>
+<li> <a href="{% url "election@view" election.uuid %}">{{election.name}}</a><em> - {{election.num_voters}} voters / {{election.num_cast_votes}} cast votes</em></li>
 {% endfor %}
 </ul>
 {% endblock %}
\ No newline at end of file
diff --git a/helios/templates/elections_voted.html b/helios/templates/elections_voted.html
index 7447eb0..f013a98 100644
--- a/helios/templates/elections_voted.html
+++ b/helios/templates/elections_voted.html
@@ -5,7 +5,7 @@
 
 <ul>
 {% for election in elections %}
-<li> <a href="{% url "helios.views.one_election_view" election.uuid %}">{{election.name}}</a></li>
+<li> <a href="{% url "election@view" election.uuid %}">{{election.name}}</a></li>
 {% endfor %}
 </ul>
 {% endblock %}
diff --git a/helios/templates/list_trustees.html b/helios/templates/list_trustees.html
index 39ddd55..47032c4 100644
--- a/helios/templates/list_trustees.html
+++ b/helios/templates/list_trustees.html
@@ -3,7 +3,7 @@
 {% block title %}Trustees for {{election.name}}{% endblock %}
 
 {% block content %}
-  <h3 class="title">{{election.name}} &mdash; Trustees <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">back to election</a>]</span></h3>
+  <h3 class="title">{{election.name}} &mdash; Trustees <span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">back to election</a>]</span></h3>
 
 <p>
     Trustees are responsible for decrypting the election result.<br />
@@ -21,11 +21,11 @@
 
 {% if admin_p %}
 <p>
-    [ <a onclick="return(confirm('Adding your own trustee requires a good bit more work to tally the election.\nYou will need to have trustees generate keypairs and safeguard their secret key.\n\nIf you are not sure what that means, we strongly recommend\nclicking Cancel and letting Helios tally the election for you.'));" href="{% url "helios.views.new_trustee" election.uuid %}">add a trustee</a> ]
+    [ <a onclick="return(confirm('Adding your own trustee requires a good bit more work to tally the election.\nYou will need to have trustees generate keypairs and safeguard their secret key.\n\nIf you are not sure what that means, we strongly recommend\nclicking Cancel and letting Helios tally the election for you.'));" href="{% url "election@trustees@new" election.uuid %}">add a trustee</a> ]
 </p>
 {% if not election.has_helios_trustee %}
 <p>
-    <a href="{% url "helios.views.new_trustee_helios" election.uuid %}">add Helios as a trustee</a>
+    <a href="{% url "election@trustees@add-helios" election.uuid %}">add Helios as a trustee</a>
 </p>
 {% endif %}
 {% endif %}
@@ -39,11 +39,11 @@
 <h5> Trustee #{{forloop.counter}}: {{t.name}} 
 {% if admin_p %}
 {% if t.secret_key %}
-{% if not election.frozen_at %}[<a onclick="return confirm('Are you sure you want to remove Helios as a trustee?');" href="{% url "helios.views.delete_trustee" election.uuid %}?uuid={{t.uuid}}">x</a>]{% endif %}
+{% if not election.frozen_at %}[<a onclick="return confirm('Are you sure you want to remove Helios as a trustee?');" href="{% url "election@trustees@delete" election.uuid %}?uuid={{t.uuid}}">x</a>]{% endif %}
 {% else %}
 ({{t.email}})
-{% if not election.frozen_at %}[<a onclick="return confirm('Are you sure you want to remove this Trustee?');" href="{% url "helios.views.delete_trustee" election.uuid %}?uuid={{t.uuid}}">x</a>]{% endif %}
-[<a onclick="return confirm('Are you sure you want to send this trustee his/her admin URL?');" href="{% url "helios.views.trustee_send_url" election.uuid t.uuid %}">send login</a>]
+{% if not election.frozen_at %}[<a onclick="return confirm('Are you sure you want to remove this Trustee?');" href="{% url "election@trustees@delete" election.uuid %}?uuid={{t.uuid}}">x</a>]{% endif %}
+[<a onclick="return confirm('Are you sure you want to send this trustee his/her admin URL?');" href="{% url "election@trustee@send-url" election.uuid t.uuid %}">send login</a>]
 {% endif %}
 {% endif %}
 </h5>
diff --git a/helios/templates/new_trustee.html b/helios/templates/new_trustee.html
index 0df1a27..75af054 100644
--- a/helios/templates/new_trustee.html
+++ b/helios/templates/new_trustee.html
@@ -1,7 +1,7 @@
 {% extends "helios/templates/cryptobase.html" %}
 
 {% block content %}
-  <h2 class="title">{{election.name}} &mdash; New Trustee <span style="font-size:0.7em;">[<a href="{% url "helios.views.list_trustees_view" election.uuid %}">cancel</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; New Trustee <span style="font-size:0.7em;">[<a href="{% url "election@trustees@view" election.uuid %}">cancel</a>]</span></h2>
   
 <form method="post" action="">
 <input type="hidden" name="csrf_token" value="{{csrf_token}}" />
diff --git a/helios/templates/release_result.html b/helios/templates/release_result.html
index cac6c64..5e467e4 100644
--- a/helios/templates/release_result.html
+++ b/helios/templates/release_result.html
@@ -2,7 +2,7 @@
 
 {% block title %}Release Result &mdash; {{election.name}}{% endblock %}
 {% block content %}
-  <h2 class="title">{{election.name}} &mdash; Release Result <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">cancel</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Release Result <span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">cancel</a>]</span></h2>
 
   <p>
     You are about to release the result for this election.
diff --git a/helios/templates/stats.html b/helios/templates/stats.html
index 37468b7..3f2bffe 100644
--- a/helios/templates/stats.html
+++ b/helios/templates/stats.html
@@ -5,11 +5,11 @@
 <h1>Admin</h1>
 
 <ul>
-<li> <a href="{% url "helios.stats_views.elections" %}">elections</a></li>
-<li> <a href="{% url "helios.stats_views.recent_votes" %}">recent votes</a></li>
-<li> <a href="{% url "helios.stats_views.recent_problem_elections" %}">recent problem elections</a></li>
+<li> <a href="{% url "stats@elections" %}">elections</a></li>
+<li> <a href="{% url "stats@recent-votes" %}">recent votes</a></li>
+<li> <a href="{% url "stats@elections-problems" %}">recent problem elections</a></li>
 </ul>
 
-<p><b>{{num_votes_in_queue}}</b> votes in queue. {% if num_votes_in_queue %}[<a href="{% url "helios.stats_views.force_queue" %}">force it</a>]{% endif %}</p>
+<p><b>{{num_votes_in_queue}}</b> votes in queue. {% if num_votes_in_queue %}[<a href="{% url "stats@force-queue" %}">force it</a>]{% endif %}</p>
 
 {% endblock %}
diff --git a/helios/templates/stats_elections.html b/helios/templates/stats_elections.html
index 7179698..e9a498e 100644
--- a/helios/templates/stats_elections.html
+++ b/helios/templates/stats_elections.html
@@ -5,7 +5,7 @@
 <h1>Elections</h1>
 
 <p>
-<form method="get" action="{% url "helios.stats_views.elections" %}">
+<form method="get" action="{% url "stats@elections" %}">
 <b>search</b>: <input type="text" name="q" value="{{q}}"/> 
 <input class="small button" type="submit" value="search" /> <a class="small button" href="?">clear search</a>
 </form>
@@ -26,7 +26,7 @@ Elections {{elections_page.start_index}} - {{elections_page.end_index}} (of {{to
 
 {% for election in elections %}
 <p>
-<b><a href="{% url "helios.views.one_election_view" election.uuid %}">{{election.name}}</a></b> by <a href="mailto:{{election.admin.info.email}}">{{election.admin.pretty_name}}</a> -- {{election.num_voters}} voters / {{election.num_cast_votes}} cast votes
+<b><a href="{% url "election@view" election.uuid %}">{{election.name}}</a></b> by <a href="mailto:{{election.admin.info.email}}">{{election.admin.pretty_name}}</a> -- {{election.num_voters}} voters / {{election.num_cast_votes}} cast votes
 </p>
 {% endfor %}
 
diff --git a/helios/templates/stats_problem_elections.html b/helios/templates/stats_problem_elections.html
index 9f8c1da..2f82f74 100644
--- a/helios/templates/stats_problem_elections.html
+++ b/helios/templates/stats_problem_elections.html
@@ -8,7 +8,7 @@ Unfrozen for more than a day.
 
 {% for election in elections %}
 <p>
-<b><a href="{% url "helios.views.one_election_view" election.uuid %}">{{election.name}}</a></b> -- {{election.num_voters}} voters
+<b><a href="{% url "election@view" election.uuid %}">{{election.name}}</a></b> -- {{election.num_voters}} voters
 </p>
 {% endfor %}
 
diff --git a/helios/templates/stats_recent_votes.html b/helios/templates/stats_recent_votes.html
index 37c0741..bf360a2 100644
--- a/helios/templates/stats_recent_votes.html
+++ b/helios/templates/stats_recent_votes.html
@@ -8,7 +8,7 @@ Last 24 hours
 
 {% for election in elections %}
 <p>
-<b><a href="{% url "helios.views.one_election_view" election.uuid %}">{{election.name}}</a></b> -- {{election.last_cast_vote}} {{election.num_recent_cast_votes}} recently cast votes
+<b><a href="{% url "election@view" election.uuid %}">{{election.name}}</a></b> -- {{election.last_cast_vote}} {{election.num_recent_cast_votes}} recently cast votes
 </p>
 {% endfor %}
 
diff --git a/helios/templates/trustee_check_sk.html b/helios/templates/trustee_check_sk.html
index 81afe42..99380d3 100644
--- a/helios/templates/trustee_check_sk.html
+++ b/helios/templates/trustee_check_sk.html
@@ -43,7 +43,7 @@ function check_sk(sk_value) {
     }
 }
 </script>
-  <h2 class="title">{{election.name}} &mdash; Trustee {{trustee.name}} &mdash; Check Secret Key <span style="font-size:0.7em;">[<a href="./home">back to trustee home</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Trustee {{trustee.name}} &mdash; Check Secret Key <span style="font-size:0.7em;">[<a href="{% url "election@trustee" election_uuid=election.uuid trustee_uuid=trustee.uuid %}">back to trustee home</a>]</span></h2>
 
 <p>
 Your public key fingerprint is: <b>{{trustee.public_key_hash}}</b>
diff --git a/helios/templates/trustee_decrypt_and_prove.html b/helios/templates/trustee_decrypt_and_prove.html
index 05153b8..cb4c2e6 100644
--- a/helios/templates/trustee_decrypt_and_prove.html
+++ b/helios/templates/trustee_decrypt_and_prove.html
@@ -195,7 +195,7 @@ function reset() {
   </div>
   
   <div id="done_div">
-      Done! <a href="{% url "helios.views.one_election_view" election.uuid %}">Back to election</a>
+      Done! <a href="{% url "election@view" election.uuid %}">Back to election</a>
   </div>
   
   <div id="error_div">
diff --git a/helios/templates/trustee_home.html b/helios/templates/trustee_home.html
index 5f6ee9c..8d6dd11 100644
--- a/helios/templates/trustee_home.html
+++ b/helios/templates/trustee_home.html
@@ -7,9 +7,9 @@
 {% if trustee.public_key_hash %}
 You have successfully uploaded your public key.<br />
 Your public key fingerprint is: <b>{{trustee.public_key_hash}}</b>.<br />
-You can <a href="{% url "helios.views.trustee_check_sk" election.uuid trustee.uuid %}">verify that you have the right secret key</a>.
+You can <a href="{% url "election@trustee@check-sk" election.uuid trustee.uuid %}">verify that you have the right secret key</a>.
 {% else %}
-<a href="{% url "helios.views.trustee_keygenerator" election.uuid trustee.uuid %}">setup your key</a>
+<a href="{% url "election@trustee@key-generator" election.uuid trustee.uuid %}">setup your key</a>
 {% endif %}
 </p>
 
@@ -19,7 +19,7 @@ You can <a href="{% url "helios.views.trustee_check_sk" election.uuid trustee.uu
 You have successfully uploaded your decryption.
 {% else %}
     The encrypted tally for this election is ready.<br />
-    <a href="{% url "helios.views.trustee_decrypt_and_prove" election.uuid trustee.uuid %}">decrypt with your key</a>
+    <a href="{% url "election@trustee@decrypt-and-prove" election.uuid trustee.uuid %}">decrypt with your key</a>
 {% endif %}
 {% else %}
 Once the tally is computed, come back here to provide your secret key for decryption purposes.<br />
diff --git a/helios/templates/voters_eligibility.html b/helios/templates/voters_eligibility.html
index 51343be..0baa6f7 100644
--- a/helios/templates/voters_eligibility.html
+++ b/helios/templates/voters_eligibility.html
@@ -2,7 +2,7 @@
 
 {% block title %}Voter Eligibility for {{election.name}}{% endblock %}
 {% block content %}
-  <h2 class="title">{{election.name}} &mdash; Voter Eligibility <span style="font-size:0.7em;">[<a href="{% url "helios.views.voters_list_pretty" election.uuid %}">back to voters</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Voter Eligibility <span style="font-size:0.7em;">[<a href="{% url "election@voters@list-pretty" election.uuid %}">back to voters</a>]</span></h2>
 
 <p>
 <em>{{election.pretty_eligibility|safe}}</em>
diff --git a/helios/templates/voters_email.html b/helios/templates/voters_email.html
index 535f753..62a6c7d 100644
--- a/helios/templates/voters_email.html
+++ b/helios/templates/voters_email.html
@@ -9,7 +9,7 @@ voter_id = '{{voter.voter_id}}';
 {% endif %}
 </script>
 
-  <h2 class="title">{{election.name}} &mdash; Contact Voters <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">back to election</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Contact Voters <span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">back to election</a>]</span></h2>
 
 {% if voter %}  
   <p>
@@ -55,7 +55,7 @@ You may tweak the subject and add a custom message using the form below.
   </div>
   
   <div id="done" style="display:none;">
-    Done, go <a href="{% url "helios.views.one_election_view" election.uuid %}">back to election</a>.
+    Done, go <a href="{% url "election@view" election.uuid %}">back to election</a>.
   </div>
 
   <div id="error" style="display:none;">
diff --git a/helios/templates/voters_list.html b/helios/templates/voters_list.html
index fffe57f..3b345a0 100644
--- a/helios/templates/voters_list.html
+++ b/helios/templates/voters_list.html
@@ -2,7 +2,7 @@
 
 {% block title %}Voters &amp; Ballot Tracking Center for {{election.name}}{% endblock %}
 {% block content %}
-  <h3 class="title">{{election.name}} &mdash; Voters and Ballot Tracking Center <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">back to election</a>]</span></h3>
+  <h3 class="title">{{election.name}} &mdash; Voters and Ballot Tracking Center <span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">back to election</a>]</span></h3>
 
 <p>
 <b>Who can vote?</b>
@@ -18,7 +18,7 @@
 <em>Your election is marked private, which means you cannot open registration up more widely</em>.<br />
 {% else %}
 You can change this setting:
-<form method="post" action="{% url "helios.views.voters_eligibility" election.uuid %}">
+<form method="post" action="{% url "election@voters@eligibility" election.uuid %}">
 <input type="hidden" name="csrf_token" value="{{csrf_token}}" />
 <input type="radio" name="eligibility" value="openreg" {% if election.openreg and not election.eligibility %}CHECKED{% endif %} /> anyone can vote<br />
 <input type="radio" name="eligibility" value="closedreg" {% if not election.openreg %}CHECKED{% endif %} /> only voters listed explicitly below can vote<br />
@@ -37,7 +37,7 @@ You can change this setting:
 {% endif %}
 
 {% if email_voters and election.frozen_at and admin_p %}
-<p><a class="button" href="{% url "helios.views.voters_email" election.uuid %}">email voters</a></p>
+<p><a class="button" href="{% url "election@voters@email" election.uuid %}">email voters</a></p>
 {% endif %}
 
 {% if election.num_voters > 20 %}
@@ -45,7 +45,7 @@ You can change this setting:
 {% if q %}
 <p><em>searching for <u>{{q}}</u>.</em> [<a href="?">clear search</a>]</p>
 {% else %}
-<form method="get" action="{% url "helios.views.voters_list_pretty" election.uuid %}"><b>search</b>: <input type="text" name="q" /> <input type="submit" value="search" /></form>
+<form method="get" action="{% url "election@voters@list-pretty" election.uuid %}"><b>search</b>: <input type="text" name="q" /> <input type="submit" value="search" /></form>
 {% endif %}
 </p>
 {% endif %}
@@ -54,7 +54,7 @@ You can change this setting:
 <!-- Add a Voter: WORK HERE-->
 {% if upload_p and not election.openreg %}
 <p>
-<a class="button" href="{% url "helios.views.voters_upload" election_uuid=election.uuid %}">bulk upload voters</a>
+<a class="button" href="{% url "election@voters@upload" election_uuid=election.uuid %}">bulk upload voters</a>
 </p>
 
 {% if voter_files %}
@@ -99,14 +99,14 @@ no votes yet
 </p>
 
 {% if voters_page.has_previous %}
-<a href="./list?page={{voters_page.previous_page_number}}&limit={{limit}}&q={{q|urlencode}}">previous {{limit}}</a> &nbsp;&nbsp;
+<a href="{% url "election@voters@list-pretty" election.uuid %}?page={{voters_page.previous_page_number}}&limit={{limit}}&q={{q|urlencode}}">previous {{limit}}</a> &nbsp;&nbsp;
 {% endif %}
 
 
 Voters {{voters_page.start_index}} - {{voters_page.end_index}} (of {{total_voters}})&nbsp;&nbsp;
 
 {% if voters_page.has_next %}
-<a href="./list?page={{voters_page.next_page_number}}&limit={{limit}}&q={{q|urlencode}}">next {{limit}}</a> &nbsp;&nbsp;
+<a href="{% url "election@voters@list-pretty" election.uuid %}?page={{voters_page.next_page_number}}&limit={{limit}}&q={{q|urlencode}}">next {{limit}}</a> &nbsp;&nbsp;
 {% endif %}
 
 <table class="pretty">
@@ -131,9 +131,9 @@ Voters {{voters_page.start_index}} - {{voters_page.end_index}} (of {{total_voter
 {% if admin_p %}
 <td style="white-space: nowrap;">
 {% if election.frozen_at %}
-[<a href="{% url "helios.views.voters_email" election.uuid %}?voter_id={{voter.voter_login_id}}">email</a>]
+[<a href="{% url "election@voters@email" election.uuid %}?voter_id={{voter.voter_login_id}}">email</a>]
 {% endif %}
-[<a onclick="return confirm('are you sure you want to remove {{voter.name}} ?');" href="{% url "helios.views.voter_delete" election.uuid voter.uuid %}">x</a>]
+[<a onclick="return confirm('are you sure you want to remove {{voter.name}} ?');" href="{% url "election@voter@delete" election.uuid voter.uuid %}">x</a>]
 </td>
 <td>{{voter.voter_login_id}}</td>
 <td>{{voter.voter_email}}</td>
@@ -143,7 +143,7 @@ Voters {{voters_page.start_index}} - {{voters_page.end_index}} (of {{total_voter
 {% if election.use_voter_aliases %}
 <td>{{voter.alias}}</td>
 {% endif %}
-<td><tt style="font-size: 1.4em;">{% if voter.vote_hash %}{{voter.vote_hash}} <span style="font-size:0.8em;">[<a href="{% url "helios.views.castvote_shortcut" vote_tinyhash=voter.vote_tinyhash %}">view</a>]</span>{% else %}&mdash;{% endif %}</tt></td>
+<td><tt style="font-size: 1.4em;">{% if voter.vote_hash %}{{voter.vote_hash}} <span style="font-size:0.8em;">[<a href="{% url "shortcut@vote" vote_tinyhash=voter.vote_tinyhash %}">view</a>]</span>{% else %}&mdash;{% endif %}</tt></td>
 </tr>
 {% endfor %}
 </table>
diff --git a/helios/templates/voters_manage.html b/helios/templates/voters_manage.html
index ccd5490..f2a83c7 100644
--- a/helios/templates/voters_manage.html
+++ b/helios/templates/voters_manage.html
@@ -1,12 +1,12 @@
 {% extends TEMPLATE_BASE %}
 
 {% block content %}
-  <h2 class="title">{{election.name}} &mdash; Manage Voters <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">back to election</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Manage Voters <span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">back to election</a>]</span></h2>
 
 <form method="get" action="{% url "helios.views.voters_search" election.uuid %}"><b>search</b>: <input type="text" name="q" /> <input type="submit" value="search" /></form>
 
 {% if upload_p %}
-<p><a href="{% url "helios.views.voters_upload" election_uuid=election.uuid %}">bulk upload voters</a></p>
+<p><a href="{% url "election@voters@upload" election_uuid=election.uuid %}">bulk upload voters</a></p>
 {% endif %}
 
 Voters {{offset_plus_one}} - {{offset_plus_limit}} &nbsp;&nbsp;
@@ -29,7 +29,7 @@ Voters {{offset_plus_one}} - {{offset_plus_limit}} &nbsp;&nbsp;
 <tr><td>{{voter.alias}}</td><td>{{voter.name}}</td><td>{{voter.voter_id}}
 {% if election.frozen_at %}
 {% else %}
-[<a onclick="return confirm('are you sure you want to remove {{voter.name}} ?');" href="{% url "helios.views.voter_delete" election.uuid voter.uuid %}">x</a>]
+[<a onclick="return confirm('are you sure you want to remove {{voter.name}} ?');" href="{% url "election@voter@delete" election.uuid voter.uuid %}">x</a>]
 {% endif %}
 </td></tr>
 {% endfor %}
diff --git a/helios/templates/voters_search.html b/helios/templates/voters_search.html
index 1adfb33..162051f 100644
--- a/helios/templates/voters_search.html
+++ b/helios/templates/voters_search.html
@@ -1,12 +1,12 @@
 {% extends TEMPLATE_BASE %}
 
 {% block content %}
-  <h2 class="title">{{election.name}} &mdash; Search Voters for '{{search_term}}' <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">back to election</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Search Voters for '{{search_term}}' <span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">back to election</a>]</span></h2>
 
 {% if voter %}
 Voter Found: {{voter.name}} ({{voter.voter_id}})<br /><br />
 {% if election.frozen_at %}
-<a href="{% url "helios.views.voters_email" election.uuid %}?voter_id={{voter.voter_id}}">email this voter</a>
+<a href="{% url "election@voters@email" election.uuid %}?voter_id={{voter.voter_id}}">email this voter</a>
 {% else %}
 once this election is frozen, you'll be able to email this voter.
 {% endif %}
diff --git a/helios/templates/voters_upload.html b/helios/templates/voters_upload.html
index a38956d..908c147 100644
--- a/helios/templates/voters_upload.html
+++ b/helios/templates/voters_upload.html
@@ -1,7 +1,7 @@
 {% extends TEMPLATE_BASE %}
 
 {% block content %}
-  <h2 class="title">{{election.name}} &mdash; Bulk Upload Voters <span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">back to election</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Bulk Upload Voters <span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">back to election</a>]</span></h2>
 
 <form method="post" action="" id="upload_form" enctype="multipart/form-data">
   <p>
diff --git a/helios/templates/voters_upload_confirm.html b/helios/templates/voters_upload_confirm.html
index c3d7c08..911cb25 100644
--- a/helios/templates/voters_upload_confirm.html
+++ b/helios/templates/voters_upload_confirm.html
@@ -1,7 +1,7 @@
 {% extends TEMPLATE_BASE %}
 
 {% block content %}
-  <h2 class="title">{{election.name}} &mdash; Bulk Upload Voters &mdash; Confirm<span style="font-size:0.7em;">[<a href="{% url "helios.views.one_election_view" election.uuid %}">back to election</a>]</span></h2>
+  <h2 class="title">{{election.name}} &mdash; Bulk Upload Voters &mdash; Confirm<span style="font-size:0.7em;">[<a href="{% url "election@view" election.uuid %}">back to election</a>]</span></h2>
 
 <p>
 You have uploaded a file of voters. The first few rows of this file are:
@@ -23,7 +23,7 @@ HOLD ON:<br />
 </p>
 <br />
 
-<a href="{% url "helios.views.voters_upload_cancel" election.uuid %}">never mind, upload a different file</a>
+<a href="{% url "election@voters@upload-cancel" election.uuid %}">never mind, upload a different file</a>
 </p>
 
 {% else %}
@@ -34,7 +34,7 @@ HOLD ON:<br />
   <input type="submit" value="Yes, let's go" />
 </form>
 
-<a href="{% url "helios.views.voters_upload_cancel" election.uuid %}">no, let me upload a different file</a>
+<a href="{% url "election@voters@upload-cancel" election.uuid %}">no, let me upload a different file</a>
 
 {% endif %}
 
diff --git a/helios/test.py b/helios/test.py
index 086f117..11fde1c 100644
--- a/helios/test.py
+++ b/helios/test.py
@@ -2,18 +2,21 @@
 Testing Helios Features
 """
 
-from helios.models import *
-from helios_auth.models import *
 import uuid
 
-def generate_voters(election, num_voters = 1000, start_with = 1):
-  # generate the user
-  for v_num in range(start_with, start_with + num_voters):
-    user = User(user_type='password', user_id='testuser%s' % v_num, name='Test User %s' % v_num)
-    user.put()
-    voter = Voter(uuid=str(uuid.uuid1()), election = election, voter_type=user.user_type, voter_id = user.user_id)
-    voter.put()
+from helios.models import Voter
+from helios_auth.models import User
+
+
+def generate_voters(election, num_voters=1000, start_with=1):
+    # generate the user
+    for v_num in range(start_with, start_with + num_voters):
+        user = User(user_type='password', user_id='testuser%s' % v_num, name='Test User %s' % v_num)
+        user.save()
+        voter = Voter(uuid=str(uuid.uuid1()), election=election, voter_type=user.user_type, voter_id=user.user_id)
+        voter.save()
+
 
 def delete_voters(election):
-  for v in Voter.get_by_election(election):
-    v.delete()
\ No newline at end of file
+    for v in Voter.get_by_election(election):
+        v.delete()
diff --git a/helios/tests.py b/helios/tests.py
index 3bd5731..157ce28 100644
--- a/helios/tests.py
+++ b/helios/tests.py
@@ -2,32 +2,28 @@
 Unit Tests for Helios
 """
 
-import unittest, datetime, re, urllib
-import django_webtest
-
-import models
-import datatypes
-
-from helios_auth import models as auth_models
-from views import ELGAMAL_PARAMS
-import views
-import utils
+import datetime
+import re
+import urllib
 
-from django.db import IntegrityError, transaction
-from django.test.client import Client
+import django_webtest
+import uuid
+from django.conf import settings
+from django.core import mail
+from django.core.files import File
 from django.test import TestCase
 from django.utils.html import escape as html_escape
 
-from django.core import mail
-from django.core.files import File
-from django.core.urlresolvers import reverse
-from django.conf import settings
-from django.core.exceptions import PermissionDenied
+import helios.datatypes as datatypes
+import helios.models as models
+import helios.utils as utils
+import helios.views as views
+from helios_auth import models as auth_models
 
-import uuid
 
 class ElectionModelTests(TestCase):
     fixtures = ['users.json']
+    allow_database_queries = True
 
     def create_election(self):
         return models.Election.get_or_create(
@@ -41,7 +37,7 @@ class ElectionModelTests(TestCase):
         self.election.questions = QUESTIONS
 
     def setup_trustee(self):
-        self.election.generate_trustee(ELGAMAL_PARAMS)
+        self.election.generate_trustee(views.ELGAMAL_PARAMS)
 
     def setup_openreg(self):
         self.election.openreg=True
@@ -114,7 +110,7 @@ class ElectionModelTests(TestCase):
         self.assertEquals(len(issues), 0)
         
     def test_helios_trustee(self):
-        self.election.generate_trustee(ELGAMAL_PARAMS)
+        self.election.generate_trustee(views.ELGAMAL_PARAMS)
 
         self.assertTrue(self.election.has_helios_trustee())
 
@@ -198,15 +194,15 @@ class ElectionModelTests(TestCase):
     def test_voter_registration(self):
         # before adding a voter
         voters = models.Voter.get_by_election(self.election)
-        self.assertTrue(len(voters) == 0)
+        self.assertEquals(0, len(voters))
 
         # make sure no voter yet
         voter = models.Voter.get_by_election_and_user(self.election, self.user)
-        self.assertTrue(voter == None)
+        self.assertIsNone(voter)
 
         # make sure no voter at all across all elections
         voters = models.Voter.get_by_user(self.user)
-        self.assertTrue(len(voters) == 0)
+        self.assertEquals(0, len(voters))
 
         # register the voter
         voter = models.Voter.register_user_in_election(self.user, self.election)
@@ -214,13 +210,13 @@ class ElectionModelTests(TestCase):
         # make sure voter is there now
         voter_2 = models.Voter.get_by_election_and_user(self.election, self.user)
 
-        self.assertFalse(voter == None)
-        self.assertFalse(voter_2 == None)
+        self.assertIsNotNone(voter)
+        self.assertIsNotNone(voter_2)
         self.assertEquals(voter, voter_2)
 
         # make sure voter is there in this call too
         voters = models.Voter.get_by_user(self.user)
-        self.assertTrue(len(voters) == 1)
+        self.assertEquals(1, len(voters))
         self.assertEquals(voter, voters[0])
 
         voter_2 = models.Voter.get_by_election_and_uuid(self.election, voter.uuid)
@@ -232,6 +228,7 @@ class ElectionModelTests(TestCase):
 
 class VoterModelTests(TestCase):
     fixtures = ['users.json', 'election.json']
+    allow_database_queries = True
 
     def setUp(self):
         self.election = models.Election.objects.get(short_name='test')
@@ -255,6 +252,7 @@ class VoterModelTests(TestCase):
 
 class CastVoteModelTests(TestCase):
     fixtures = ['users.json', 'election.json']
+    allow_database_queries = True
 
     def setUp(self):
         self.election = models.Election.objects.get(short_name='test')
@@ -268,10 +266,11 @@ class CastVoteModelTests(TestCase):
 
 class DatatypeTests(TestCase):
     fixtures = ['users.json', 'election.json']
+    allow_database_queries = True
 
     def setUp(self):
         self.election = models.Election.objects.all()[0]
-        self.election.generate_trustee(ELGAMAL_PARAMS)
+        self.election.generate_trustee(views.ELGAMAL_PARAMS)
 
     def test_instantiate(self):
         ld_obj = datatypes.LDObject.instantiate(self.election.get_helios_trustee(), '2011/01/Trustee')
@@ -334,6 +333,7 @@ class DataFormatBlackboxTests(object):
 
 class LegacyElectionBlackboxTests(DataFormatBlackboxTests, TestCase):
     fixtures = ['legacy-data.json']
+    allow_database_queries = True
     EXPECTED_ELECTION_FILE = 'helios/fixtures/legacy-election-expected.json'
     EXPECTED_ELECTION_METADATA_FILE = 'helios/fixtures/legacy-election-metadata-expected.json'
     EXPECTED_VOTERS_FILE = 'helios/fixtures/legacy-election-voters-expected.json'
@@ -348,6 +348,13 @@ class LegacyElectionBlackboxTests(DataFormatBlackboxTests, TestCase):
 #    EXPECTED_BALLOTS_FILE = 'helios/fixtures/v3.1-ballots-expected.json'
 
 class WebTest(django_webtest.WebTest):
+    def assertStatusCode(self, response, status_code):
+        if hasattr(response, 'status_code'):
+            assert response.status_code == status_code, response.status_code
+        else:
+            assert response.status_int == status_code, response.status_int
+
+
     def assertRedirects(self, response, url):
         """
         reimplement this in case it's a WebOp response
@@ -355,38 +362,23 @@ class WebTest(django_webtest.WebTest):
         thus the localhost exception
         """
         if hasattr(response, 'location'):
-            assert url in response.location
+            assert url in response.location, response.location
         else:
-            assert url in response._headers['location'][1]
-
-        if hasattr(response, 'status_code'):
-            assert response.status_code == 302
-        else:
-            assert response.status_int == 302
-
-        #self.assertEqual(response.status_code, 302)
-
+            assert url in response['location'], response['location']
+        self.assertStatusCode(response, 302)
         #return super(django_webtest.WebTest, self).assertRedirects(response, url)
-        #if hasattr(response, 'status_code') and hasattr(response, 'location'):
-
-
         #assert url in response.location, "redirected to %s instead of %s" % (response.location, url)
 
+
     def assertContains(self, response, text):
-        if hasattr(response, 'status_code'):
-            assert response.status_code == 200
-#            return super(django_webtest.WebTest, self).assertContains(response, text)
-        else:
-            assert response.status_int == 200
+        self.assertStatusCode(response, 200)
 
-        
         if hasattr(response, "testbody"):
             assert text in response.testbody, "missing text %s" % text
+        elif hasattr(response, "body"):
+            assert text in response.body, "missing text %s" % text
         else:
-            if hasattr(response, "body"):
-                assert text in response.body, "missing text %s" % text        
-            else:
-                assert text in response.content, "missing text %s" % text
+            assert text in response.content, "missing text %s" % text
 
 
 ##
@@ -395,31 +387,23 @@ class WebTest(django_webtest.WebTest):
 
 class ElectionBlackboxTests(WebTest):
     fixtures = ['users.json', 'election.json']
+    allow_database_queries = True
 
     def setUp(self):
         self.election = models.Election.objects.all()[0]
         self.user = auth_models.User.objects.get(user_id='ben@adida.net', user_type='google')
 
-    def assertContains(self, response, text):
-        if hasattr(response, 'status_code'):
-            assert response.status_code == 200
-#            return super(django_webtest.WebTest, self).assertContains(response, text)
-        else:
-            assert response.status_int == 200
-
-        
-        if hasattr(response, "testbody"):
-            assert text in response.testbody, "missing text %s" % text
-        else:
-            if hasattr(response, "body"):
-                assert text in response.body, "missing text %s" % text        
-            else:
-                assert text in response.content, "missing text %s" % text
-
-    def setup_login(self):
+    def setup_login(self, from_scratch=False, **kwargs):
+        if from_scratch:
+            # a bogus call to set up the session
+            self.client.get("/")
         # set up the session
         session = self.client.session
-        session['user'] = {'type': self.user.user_type, 'user_id': self.user.user_id}
+        if kwargs:
+            user = auth_models.User.objects.get(**kwargs)
+        else:
+            user = self.user
+        session['user'] = {'type': user.user_type, 'user_id': user.user_id}
         session.save()
 
         # set up the app, too
@@ -438,11 +422,11 @@ class ElectionBlackboxTests(WebTest):
 
     def test_election_404(self):
         response = self.client.get("/helios/elections/foobar")
-        self.assertEquals(response.status_code, 404)
+        self.assertStatusCode(response, 404)
 
     def test_election_bad_trustee(self):
         response = self.client.get("/helios/t/%s/foobar@bar.com/badsecret" % self.election.short_name)
-        self.assertEquals(response.status_code, 404)
+        self.assertStatusCode(response, 404)
 
     def test_get_election_shortcut(self):
         response = self.client.get("/helios/e/%s" % self.election.short_name, follow=True)
@@ -491,10 +475,7 @@ class ElectionBlackboxTests(WebTest):
         self.assertRedirects(response, "/auth/?return_url=/helios/elections/new")
     
     def test_election_edit(self):
-        # a bogus call to set up the session
-        self.client.get("/")
-
-        self.setup_login()
+        self.setup_login(from_scratch=True)
         response = self.client.get("/helios/elections/%s/edit" % self.election.uuid)
         response = self.client.post("/helios/elections/%s/edit" % self.election.uuid, {
                 "short_name" : self.election.short_name + "-2",
@@ -510,14 +491,31 @@ class ElectionBlackboxTests(WebTest):
         new_election = models.Election.objects.get(uuid = self.election.uuid)
         self.assertEquals(new_election.short_name, self.election.short_name + "-2")
 
-    def _setup_complete_election(self, election_params={}):
-        "do the setup part of a whole election"
+    def test_get_election_stats(self):
+        self.setup_login(from_scratch=True, user_id='mccio@github.com', user_type='google')
+        response = self.client.get("/helios/stats/", follow=False)
+        self.assertStatusCode(response, 200)
+        response = self.client.get("/helios/stats/force-queue", follow=False)
+        self.assertRedirects(response, "/helios/stats/")
+        response = self.client.get("/helios/stats/elections", follow=False)
+        self.assertStatusCode(response, 200)
+        response = self.client.get("/helios/stats/problem-elections", follow=False)
+        self.assertStatusCode(response, 200)
+        response = self.client.get("/helios/stats/recent-votes", follow=False)
+        self.assertStatusCode(response, 200)
+        self.clear_login()
+        response = self.client.get("/helios/stats/", follow=False)
+        self.assertStatusCode(response, 403)
+        self.setup_login()
+        response = self.client.get("/helios/stats/", follow=False)
+        self.assertStatusCode(response, 403)
+        self.clear_login()
 
-        # a bogus call to set up the session
-        self.client.get("/")
+    def _setup_complete_election(self, election_params=None):
+        "do the setup part of a whole election"
 
         # REPLACE with params?
-        self.setup_login()
+        self.setup_login(from_scratch=True)
 
         # create the election
         full_election_params = {
@@ -532,12 +530,14 @@ class ElectionBlackboxTests(WebTest):
         }
 
         # override with the given
-        full_election_params.update(election_params)
+        full_election_params.update(election_params or {})
 
         response = self.client.post("/helios/elections/new", full_election_params)
 
         # we are redirected to the election, let's extract the ID out of the URL
-        election_id = re.search('/elections/([^/]+)/', str(response['Location'])).group(1)
+        election_id = re.search('/elections/([^/]+)/', str(response['Location']))
+        self.assertIsNotNone(election_id, "Election id not found in redirect: %s" % str(response['Location']))
+        election_id = election_id.group(1)
 
         # helios is automatically added as a trustee
 
@@ -577,7 +577,7 @@ class ElectionBlackboxTests(WebTest):
         self.assertContains(response, '"uuid": "%s"' % single_voter.uuid)
 
         response = self.client.get("/helios/elections/%s/voters/foobar" % election_id)
-        self.assertEquals(response.status_code, 404)
+        self.assertStatusCode(response, 404)
         
         # add questions
         response = self.client.post("/helios/elections/%s/save_questions" % election_id, {
@@ -670,7 +670,7 @@ class ElectionBlackboxTests(WebTest):
         # at this point an email should have gone out to the user
         # at position num_messages after, since that was the len() before we cast this ballot
         email_message = mail.outbox[len(mail.outbox) - 1]
-        url = re.search('http://[^/]+(/[^ \n]*)', email_message.body).group(1)
+        url = re.search('https?://[^/]+(/[^ \n]*)', email_message.body).group(1)
 
         # check that we can get at that URL
         if not need_login:
@@ -690,7 +690,7 @@ class ElectionBlackboxTests(WebTest):
                 login_form['password'] = '  ' + password + '      '
                 login_form.submit()
             
-        response = self.app.get(url)
+        response = self.app.get(url, auto_follow=True)
         self.assertContains(response, ballot.hash)
         self.assertContains(response, html_escape(encrypted_vote))
 
@@ -723,7 +723,7 @@ class ElectionBlackboxTests(WebTest):
 
         # check that we can't get the tally yet
         response = self.client.get("/helios/elections/%s/result" % election_id)
-        self.assertEquals(response.status_code, 403)
+        self.assertStatusCode(response, 403)
 
         # release
         response = self.client.post("/helios/elections/%s/release_result" % election_id, {
@@ -776,8 +776,7 @@ class ElectionBlackboxTests(WebTest):
 
     def test_election_voters_eligibility(self):
         # create the election
-        self.client.get("/")
-        self.setup_login()
+        self.setup_login(from_scratch=True)
         response = self.client.post("/helios/elections/new", {
                 "short_name" : "test-eligibility",
                 "name" : "Test Eligibility",
@@ -788,7 +787,9 @@ class ElectionBlackboxTests(WebTest):
                 "private_p" : "False",
                 'csrf_token': self.client.session['csrf_token']})
 
-        election_id = re.match("(.*)/elections/(.*)/view", response['Location']).group(2)
+        election_id = re.match("(.*)/elections/(.*)/view", str(response['Location']))
+        self.assertIsNotNone(election_id, "Election id not found in redirect: %s" % str(response['Location']))
+        election_id = election_id.group(2)
 
         # update eligiblity
         response = self.client.post("/helios/elections/%s/voters/eligibility" % election_id, {
diff --git a/helios/url_names.py b/helios/url_names.py
new file mode 100644
index 0000000..319a9be
--- /dev/null
+++ b/helios/url_names.py
@@ -0,0 +1,27 @@
+from helios import election_url_names as election, stats_url_names as stats
+
+__all__ = [
+    "election", "stats",
+    "COOKIE_TEST", "COOKIE_TEST_2", "COOKIE_NO",
+    "ELECTION_SHORTCUT", "ELECTION_SHORTCUT_VOTE", "CAST_VOTE_SHORTCUT",
+    "TRUSTEE_LOGIN",
+    "ELECTIONS_PARAMS", "ELECTIONS_VERIFIER", "ELECTIONS_VERIFIER_SINGLE_BALLOT",
+    "ELECTIONS_NEW", "ELECTIONS_ADMINISTERED", "ELECTIONS_VOTED",
+]
+
+COOKIE_TEST="cookie@test"
+COOKIE_TEST_2="cookie@test2"
+COOKIE_NO="cookie@no"
+
+ELECTION_SHORTCUT="shortcut@election"
+ELECTION_SHORTCUT_VOTE="shortcut@election@vote"
+CAST_VOTE_SHORTCUT="shortcut@vote"
+
+TRUSTEE_LOGIN="trustee@login"
+
+ELECTIONS_PARAMS="elections@params"
+ELECTIONS_VERIFIER="elections@verifier"
+ELECTIONS_VERIFIER_SINGLE_BALLOT="elections@verifier@single-ballot"
+ELECTIONS_NEW="elections@new"
+ELECTIONS_ADMINISTERED="elections@administered"
+ELECTIONS_VOTED="elections@voted"
diff --git a/helios/urls.py b/helios/urls.py
index 8effba1..6d278a9 100644
--- a/helios/urls.py
+++ b/helios/urls.py
@@ -1,35 +1,34 @@
 # -*- coding: utf-8 -*-
-from django.conf.urls import patterns, include
+from django.conf.urls import url, include
 
-from views import *
+import url_names as names
+import views
 
-urlpatterns = patterns('',
-  (r'^autologin$', admin_autologin),
-  (r'^testcookie$', test_cookie),
-  (r'^testcookie_2$', test_cookie_2),
-  (r'^nocookies$', nocookies),
-  (r'^stats/', include('helios.stats_urls')),
+urlpatterns = [
+  url(r'^autologin$', views.admin_autologin),
+  url(r'^testcookie$', views.test_cookie, name=names.COOKIE_TEST),
+  url(r'^testcookie_2$', views.test_cookie_2, name=names.COOKIE_TEST_2),
+  url(r'^nocookies$', views.nocookies, name=names.COOKIE_NO),
+  url(r'^stats/', include('helios.stats_urls')),
 
   # election shortcut by shortname
-  (r'^e/(?P<election_short_name>[^/]+)$', election_shortcut),
-  (r'^e/(?P<election_short_name>[^/]+)/vote$', election_vote_shortcut),
+  url(r'^e/(?P<election_short_name>[^/]+)$', views.election_shortcut, name=names.ELECTION_SHORTCUT),
+  url(r'^e/(?P<election_short_name>[^/]+)/vote$', views.election_vote_shortcut, name=names.ELECTION_SHORTCUT_VOTE),
 
   # vote shortcut
-  (r'^v/(?P<vote_tinyhash>[^/]+)$', castvote_shortcut),
+  url(r'^v/(?P<vote_tinyhash>[^/]+)$', views.castvote_shortcut, name=names.CAST_VOTE_SHORTCUT),
   
   # trustee login
-  (r'^t/(?P<election_short_name>[^/]+)/(?P<trustee_email>[^/]+)/(?P<trustee_secret>[^/]+)$', trustee_login),
+  url(r'^t/(?P<election_short_name>[^/]+)/(?P<trustee_email>[^/]+)/(?P<trustee_secret>[^/]+)$', views.trustee_login,
+      name=names.TRUSTEE_LOGIN),
   
   # election
-  (r'^elections/params$', election_params),
-  (r'^elections/verifier$', election_verifier),
-  (r'^elections/single_ballot_verifier$', election_single_ballot_verifier),
-  (r'^elections/new$', election_new),
-  (r'^elections/administered$', elections_administered),
-  (r'^elections/voted$', elections_voted),
+  url(r'^elections/params$', views.election_params, name=names.ELECTIONS_PARAMS),
+  url(r'^elections/verifier$', views.election_verifier, name=names.ELECTIONS_VERIFIER),
+  url(r'^elections/single_ballot_verifier$', views.election_single_ballot_verifier, name=names.ELECTIONS_VERIFIER_SINGLE_BALLOT),
+  url(r'^elections/new$', views.election_new, name=names.ELECTIONS_NEW),
+  url(r'^elections/administered$', views.elections_administered, name=names.ELECTIONS_ADMINISTERED),
+  url(r'^elections/voted$', views.elections_voted, name=names.ELECTIONS_VOTED),
   
-  (r'^elections/(?P<election_uuid>[^/]+)', include('helios.election_urls')),
-  
-)
-
-
+  url(r'^elections/(?P<election_uuid>[^/]+)', include('helios.election_urls')),
+]
diff --git a/helios/view_utils.py b/helios/view_utils.py
index 3e3fb5a..26da704 100644
--- a/helios/view_utils.py
+++ b/helios/view_utils.py
@@ -4,22 +4,16 @@ Utilities for all views
 Ben Adida (12-30-2008)
 """
 
-from django.template import Context, Template, loader
-from django.http import HttpResponse, Http404
+from django.conf import settings
+from django.http import HttpResponse
 from django.shortcuts import render_to_response
-
-import utils
-
-from helios import datatypes
-
+from django.template import loader
 # nicely update the wrapper function
 from functools import update_wrapper
 
-from helios_auth.security import get_user
-
 import helios
-
-from django.conf import settings
+import utils
+from helios_auth.security import get_user
 
 ##
 ## BASICS
@@ -33,14 +27,14 @@ FAILURE = HttpResponse("FAILURE")
 ##
 ## template abstraction
 ##
-def prepare_vars(request, vars):
-  vars_with_user = vars.copy()
+def prepare_vars(request, values):
+  vars_with_user = values.copy() if values is not None else {}
   vars_with_user['user'] = get_user(request)
-  
+
   # csrf protection
   if request.session.has_key('csrf_token'):
     vars_with_user['csrf_token'] = request.session['csrf_token']
-    
+
   vars_with_user['utils'] = utils
   vars_with_user['settings'] = settings
   vars_with_user['HELIOS_STATIC'] = '/static/helios/helios'
@@ -50,32 +44,32 @@ def prepare_vars(request, vars):
 
   return vars_with_user
 
-def render_template(request, template_name, vars = {}, include_user=True):
-  t = loader.get_template(template_name + '.html')
-  
-  vars_with_user = prepare_vars(request, vars)
-  
+
+def render_template(request, template_name, values = None, include_user=True):
+  vars_with_user = prepare_vars(request, values)
+
   if not include_user:
     del vars_with_user['user']
-  
+
   return render_to_response('helios/templates/%s.html' % template_name, vars_with_user)
-  
-def render_template_raw(request, template_name, vars={}):
+
+
+def render_template_raw(request, template_name, values=None):
   t = loader.get_template(template_name)
-  
+
   # if there's a request, prep the vars, otherwise can't do it.
   if request:
-    full_vars = prepare_vars(request, vars)
+    full_vars = prepare_vars(request, values)
   else:
-    full_vars = vars
+    full_vars = values or {}
 
-  c = Context(full_vars)  
-  return t.render(c)
+  return t.render(context=full_vars, request=request)
 
 
 def render_json(json_txt):
   return HttpResponse(json_txt, "application/json")
 
+
 # decorator
 def return_json(func):
     """
@@ -92,4 +86,3 @@ def return_json(func):
         raise e
 
     return update_wrapper(convert_to_json,func)
-    
diff --git a/helios/views.py b/helios/views.py
index 3337459..b62f6ee 100644
--- a/helios/views.py
+++ b/helios/views.py
@@ -5,7 +5,7 @@ Helios Django Views
 Ben Adida (ben@adida.net)
 """
 
-from django.core.urlresolvers import reverse
+from django.urls import reverse
 from django.core.paginator import Paginator
 from django.core.exceptions import PermissionDenied
 from django.http import HttpResponse, Http404, HttpResponseRedirect, HttpResponseForbidden
@@ -18,12 +18,13 @@ import urllib, os, base64
 from crypto import algs, electionalgs, elgamal
 from crypto import utils as cryptoutils
 from workflows import homomorphic
-from helios import utils, VOTERS_EMAIL, VOTERS_UPLOAD
+from helios import utils, VOTERS_EMAIL, VOTERS_UPLOAD, url_names
 from view_utils import SUCCESS, FAILURE, return_json, render_template, render_template_raw
 
 from helios_auth.security import check_csrf, login_required, get_user, save_in_session_across_logouts
 from helios_auth.auth_systems import AUTH_SYSTEMS, can_list_categories
 from helios_auth.models import AuthenticationExpired
+import helios_auth.url_names as helios_auth_urls
 
 from helios_auth import views as auth_views
 
@@ -57,16 +58,16 @@ ELGAMAL_PARAMS_LD_OBJECT = datatypes.LDObject.instantiate(ELGAMAL_PARAMS, dataty
 from django.conf import settings
 
 def get_election_url(election):
-  return settings.URL_HOST + reverse(election_shortcut, args=[election.short_name])  
+  return settings.URL_HOST + reverse(url_names.ELECTION_SHORTCUT, args=[election.short_name])
 
 def get_election_badge_url(election):
-  return settings.URL_HOST + reverse(election_badge, args=[election.uuid])  
+  return settings.URL_HOST + reverse(url_names.election.ELECTION_BADGE, args=[election.uuid])
 
 def get_election_govote_url(election):
-  return settings.URL_HOST + reverse(election_vote_shortcut, args=[election.short_name])  
+  return settings.URL_HOST + reverse(url_names.ELECTION_SHORTCUT_VOTE, args=[election.short_name])
 
 def get_castvote_url(cast_vote):
-  return settings.URL_HOST + reverse(castvote_shortcut, args=[cast_vote.vote_tinyhash])
+  return settings.URL_HOST + reverse(url_names.CAST_VOTE_SHORTCUT, args=[cast_vote.vote_tinyhash])
 
 
 ##
@@ -76,7 +77,7 @@ def user_reauth(request, user):
   # FIXME: should we be wary of infinite redirects here, and
   # add a parameter to prevent it? Maybe.
   login_url = "%s%s?%s" % (settings.SECURE_URL_HOST,
-                           reverse(auth_views.start, args=[user.user_type]),
+                           reverse(helios_auth_urls.AUTH_START, args=[user.user_type]),
                            urllib.urlencode({'return_url':
                                                request.get_full_path()}))
   return HttpResponseRedirect(login_url)
@@ -116,16 +117,16 @@ def election_single_ballot_verifier(request):
 def election_shortcut(request, election_short_name):
   election = Election.get_by_short_name(election_short_name)
   if election:
-    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
+    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
   else:
     raise Http404
 
 # a hidden view behind the shortcut that performs the actual perm check
 @election_view()
 def _election_vote_shortcut(request, election):
-  vote_url = "%s/booth/vote.html?%s" % (settings.SECURE_URL_HOST, urllib.urlencode({'election_url' : reverse(one_election, args=[election.uuid])}))
+  vote_url = "%s/booth/vote.html?%s" % (settings.SECURE_URL_HOST, urllib.urlencode({'election_url' : reverse(url_names.election.ELECTION_HOME, args=[election.uuid])}))
   
-  test_cookie_url = "%s?%s" % (reverse(test_cookie), urllib.urlencode({'continue_url' : vote_url}))
+  test_cookie_url = "%s?%s" % (reverse(url_names.COOKIE_TEST), urllib.urlencode({'continue_url' : vote_url}))
 
   return HttpResponseRedirect(test_cookie_url)
   
@@ -208,7 +209,7 @@ def election_new(request):
         try:
           election = Election.objects.create(**election_params)
           election.generate_trustee(ELGAMAL_PARAMS)
-          return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
+          return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
         except IntegrityError:
           error = "An election with short name %s already exists" % election_params['short_name']
       else:
@@ -241,7 +242,7 @@ def one_election_edit(request, election):
         setattr(election, attr_name, clean_data[attr_name])
       try:
         election.save()
-        return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
+        return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
       except IntegrityError:
         error = "An election with short name %s already exists" % clean_data['short_name']
 
@@ -264,7 +265,7 @@ def one_election_extend(request, election):
       election.voting_extended_until = clean_data['voting_extended_until']
       election.save()
         
-      return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
+      return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
   
   return render_template(request, "election_extend", {'election_form' : election_form, 'election' : election})
 
@@ -303,9 +304,9 @@ def one_election_view(request, election):
   election_badge_url = get_election_badge_url(election)
   status_update_message = None
 
-  vote_url = "%s/booth/vote.html?%s" % (settings.SECURE_URL_HOST, urllib.urlencode({'election_url' : reverse(one_election, args=[election.uuid])}))
+  vote_url = "%s/booth/vote.html?%s" % (settings.SECURE_URL_HOST, urllib.urlencode({'election_url' : reverse(url_names.election.ELECTION_HOME, args=[election.uuid])}))
 
-  test_cookie_url = "%s?%s" % (reverse(test_cookie), urllib.urlencode({'continue_url' : vote_url}))
+  test_cookie_url = "%s?%s" % (reverse(url_names.COOKIE_TEST), urllib.urlencode({'continue_url' : vote_url}))
   
   if user:
     voter = Voter.get_by_election_and_user(election, user)
@@ -352,20 +353,20 @@ def one_election_view(request, election):
 def test_cookie(request):
   continue_url = request.GET['continue_url']
   request.session.set_test_cookie()
-  next_url = "%s?%s" % (reverse(test_cookie_2), urllib.urlencode({'continue_url': continue_url}))
+  next_url = "%s?%s" % (reverse(url_names.COOKIE_TEST_2), urllib.urlencode({'continue_url': continue_url}))
   return HttpResponseRedirect(settings.SECURE_URL_HOST + next_url)  
 
 def test_cookie_2(request):
   continue_url = request.GET['continue_url']
 
   if not request.session.test_cookie_worked():
-    return HttpResponseRedirect(settings.SECURE_URL_HOST + ("%s?%s" % (reverse(nocookies), urllib.urlencode({'continue_url': continue_url}))))
+    return HttpResponseRedirect(settings.SECURE_URL_HOST + ("%s?%s" % (reverse(url_names.COOKIE_NO), urllib.urlencode({'continue_url': continue_url}))))
 
   request.session.delete_test_cookie()
   return HttpResponseRedirect(continue_url)  
 
 def nocookies(request):
-  retest_url = "%s?%s" % (reverse(test_cookie), urllib.urlencode({'continue_url' : request.GET['continue_url']}))
+  retest_url = "%s?%s" % (reverse(url_names.COOKIE_TEST), urllib.urlencode({'continue_url' : request.GET['continue_url']}))
   return render_template(request, 'nocookies', {'retest_url': retest_url})
 
 ##
@@ -399,7 +400,7 @@ def new_trustee(request, election):
     
     trustee = Trustee(uuid = str(uuid.uuid1()), election = election, name=name, email=email)
     trustee.save()
-    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(list_trustees_view, args=[election.uuid]))
+    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_TRUSTEES_VIEW, args=[election.uuid]))
 
 @election_admin(frozen=False)
 def new_trustee_helios(request, election):
@@ -407,13 +408,13 @@ def new_trustee_helios(request, election):
   Make Helios a trustee of the election
   """
   election.generate_trustee(ELGAMAL_PARAMS)
-  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(list_trustees_view, args=[election.uuid]))
+  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_TRUSTEES_VIEW, args=[election.uuid]))
   
 @election_admin(frozen=False)
 def delete_trustee(request, election):
   trustee = Trustee.get_by_election_and_uuid(election, request.GET['uuid'])
   trustee.delete()
-  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(list_trustees_view, args=[election.uuid]))
+  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_TRUSTEES_VIEW, args=[election.uuid]))
   
 def trustee_login(request, election_short_name, trustee_email, trustee_secret):
   election = Election.get_by_short_name(election_short_name)
@@ -423,7 +424,7 @@ def trustee_login(request, election_short_name, trustee_email, trustee_secret):
     if trustee:
       if trustee.secret == trustee_secret:
         set_logged_in_trustee(request, trustee)
-        return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(trustee_home, args=[election.uuid, trustee.uuid]))
+        return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_TRUSTEE_HOME, args=[election.uuid, trustee.uuid]))
     # bad secret or no such trustee
     raise Http404("Trustee not recognized.")
   raise Http404("No election {} found.".format(election_short_name))
@@ -432,7 +433,7 @@ def trustee_login(request, election_short_name, trustee_email, trustee_secret):
 def trustee_send_url(request, election, trustee_uuid):
   trustee = Trustee.get_by_election_and_uuid(election, trustee_uuid)
   
-  url = settings.SECURE_URL_HOST + reverse(trustee_login, args=[election.short_name, trustee.email, trustee.secret])
+  url = settings.SECURE_URL_HOST + reverse(url_names.TRUSTEE_LOGIN, args=[election.short_name, trustee.email, trustee.secret])
   
   body = """
 
@@ -449,7 +450,7 @@ Helios
   utils.send_email(settings.SERVER_EMAIL, ["%s <%s>" % (trustee.name, trustee.email)], 'your trustee homepage for %s' % election.name, body)
 
   logging.info("URL %s " % url)
-  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(list_trustees_view, args = [election.uuid]))
+  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_TRUSTEES_VIEW, args = [election.uuid]))
 
 @trustee_check
 def trustee_home(request, election, trustee):
@@ -482,7 +483,7 @@ def trustee_upload_pk(request, election, trustee):
       # oh well, no message sent
       pass
     
-  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(trustee_home, args=[election.uuid, trustee.uuid]))
+  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_TRUSTEE_HOME, args=[election.uuid, trustee.uuid]))
 
 ##
 ## Ballot Management
@@ -530,14 +531,14 @@ def one_election_cast(request, election):
   on a GET, this is a cancellation, on a POST it's a cast
   """
   if request.method == "GET":
-    return HttpResponseRedirect("%s%s" % (settings.SECURE_URL_HOST, reverse(one_election_view, args = [election.uuid])))
+    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args = [election.uuid]))
     
   user = get_user(request)
   encrypted_vote = request.POST['encrypted_vote']
 
   save_in_session_across_logouts(request, 'encrypted_vote', encrypted_vote)
 
-  return HttpResponseRedirect("%s%s" % (settings.SECURE_URL_HOST, reverse(one_election_cast_confirm, args=[election.uuid])))
+  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_cast_confirm, args=[election.uuid]))
 
 @election_view(allow_logins=True)
 def password_voter_login(request, election):
@@ -559,7 +560,7 @@ def password_voter_login(request, election):
     # if user logged in somehow in the interim, e.g. using the login link for administration,
     # then go!
     if user_can_see_election(request, election):
-      return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args = [election.uuid]))
+      return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args = [election.uuid]))
 
     password_login_form = forms.VoterPasswordForm()
     return render_template(request, 'password_voter_login',
@@ -574,7 +575,7 @@ def password_voter_login(request, election):
     # login depending on whether this is a private election
     # cause if it's private the login is happening on the front page
     if election.private_p:
-      login_url = reverse(password_voter_login, args=[election.uuid])
+      login_url = reverse(url_names.election.ELECTION_PASSWORD_VOTER_LOGIN, args=[election.uuid])
     else:
       login_url = reverse(one_election_cast_confirm, args=[election.uuid])
 
@@ -742,7 +743,7 @@ def one_election_cast_confirm(request, election):
     # remove the vote from the store
     del request.session['encrypted_vote']
     
-    return HttpResponseRedirect("%s%s" % (settings.URL_HOST, reverse(one_election_cast_done, args=[election.uuid])))
+    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_cast_done, args=[election.uuid]))
   
 @election_view()
 def one_election_cast_done(request, election):
@@ -930,7 +931,7 @@ def one_election_set_featured(request, election):
   election.featured_p = featured_p
   election.save()
   
-  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
+  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
 
 @election_admin()
 def one_election_archive(request, election):
@@ -944,7 +945,7 @@ def one_election_archive(request, election):
     
   election.save()
 
-  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
+  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
   
 @election_admin()
 def one_election_copy(request, election):
@@ -978,7 +979,7 @@ def one_election_copy(request, election):
   
 
   new_election.generate_trustee(ELGAMAL_PARAMS)
-  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[new_election.uuid]))
+  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[new_election.uuid]))
 
 # changed from admin to view because 
 # anyone can see the questions, the administration aspect is now
@@ -1017,7 +1018,7 @@ def one_election_register(request, election):
   if not voter:
     voter = _register_voter(election, user)
     
-  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
+  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
 
 @election_admin(frozen=False)
 def one_election_save_questions(request, election):
@@ -1046,7 +1047,7 @@ def one_election_freeze(request, election):
     election.freeze()
 
     if get_user(request):
-      return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
+      return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
     else:
       return SUCCESS    
 
@@ -1062,7 +1063,7 @@ def one_election_compute_tally(request, election):
   tallying is done all at a time now
   """
   if not _check_election_tally_type(election):
-    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view,args=[election.election_id]))
+    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW,args=[election.election_id]))
 
   if request.method == "GET":
     return render_template(request, 'election_compute_tally', {'election': election})
@@ -1077,12 +1078,12 @@ def one_election_compute_tally(request, election):
 
   tasks.election_compute_tally.delay(election_id = election.id)
 
-  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view,args=[election.uuid]))
+  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW,args=[election.uuid]))
 
 @trustee_check
 def trustee_decrypt_and_prove(request, election, trustee):
   if not _check_election_tally_type(election) or election.encrypted_tally == None:
-    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view,args=[election.uuid]))
+    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW,args=[election.uuid]))
     
   return render_template(request, 'trustee_decrypt_and_prove', {'election': election, 'trustee': trustee})
   
@@ -1131,7 +1132,7 @@ def release_result(request, election):
     if request.POST.get('send_email', ''):
       return HttpResponseRedirect("%s?%s" % (settings.SECURE_URL_HOST + reverse(voters_email, args=[election.uuid]),urllib.urlencode({'template': 'result'})))
     else:
-      return HttpResponseRedirect("%s" % (settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid])))
+      return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
 
   # if just viewing the form or the form is not valid
   return render_template(request, 'release_result', {'election': election})
@@ -1150,7 +1151,7 @@ def combine_decryptions(request, election):
     election.combine_decryptions()
     election.save()
 
-    return HttpResponseRedirect("%s" % (settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid])))
+    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
 
   # if just viewing the form or the form is not valid
   return render_template(request, 'combine_decryptions', {'election': election})
@@ -1158,7 +1159,7 @@ def combine_decryptions(request, election):
 @election_admin(frozen=True)
 def one_election_set_result_and_proof(request, election):
   if election.tally_type != "homomorphic" or election.encrypted_tally == None:
-    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view,args=[election.election_id]))
+    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.election_id]))
 
   # FIXME: check csrf
   
@@ -1167,7 +1168,7 @@ def one_election_set_result_and_proof(request, election):
   election.save()
 
   if get_user(request):
-    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
+    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
   else:
     return SUCCESS
   
@@ -1326,12 +1327,12 @@ def voters_upload_cancel(request, election):
     vf.delete()
   del request.session['voter_file_id']
 
-  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
+  return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
 
 @election_admin(frozen=True)
 def voters_email(request, election):
   if not VOTERS_EMAIL:
-    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
+    return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
   TEMPLATES = [
     ('vote', 'Time to Vote'),
     ('simple', 'Simple'),
@@ -1408,7 +1409,7 @@ def voters_email(request, election):
         tasks.voters_email.delay(election_id = election.id, subject_template = subject_template, body_template = body_template, extra_vars = extra_vars, voter_constraints_include = voter_constraints_include, voter_constraints_exclude = voter_constraints_exclude)
 
       # this batch process is all async, so we can return a nice note
-      return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(one_election_view, args=[election.uuid]))
+      return HttpResponseRedirect(settings.SECURE_URL_HOST + reverse(url_names.election.ELECTION_VIEW, args=[election.uuid]))
     
   return render_template(request, "voters_email", {
       'email_form': email_form, 'election': election,
diff --git a/helios/widgets.py b/helios/widgets.py
index 7eff891..9eff2f4 100644
--- a/helios/widgets.py
+++ b/helios/widgets.py
@@ -5,8 +5,7 @@ Widget for datetime split, with calendar for date, and drop-downs for times.
 from django import forms
 from django.db import models
 from django.template.loader import render_to_string
-from django.forms.widgets import Select, MultiWidget, DateInput, TextInput, Widget
-from django.forms.extras.widgets import SelectDateWidget
+from django.forms.widgets import Select, MultiWidget, DateInput, TextInput, Widget, SelectDateWidget
 from time import strftime
 
 import re
@@ -36,19 +35,20 @@ class SelectTimeWidget(Widget):
     
     Also allows user-defined increments for minutes/seconds
     """
+    template_name = ''
     hour_field = '%s_hour'
     minute_field = '%s_minute'
     meridiem_field = '%s_meridiem'
     twelve_hr = False # Default to 24hr.
-    
+
     def __init__(self, attrs=None, hour_step=None, minute_step=None, twelve_hr=False):
         """
         hour_step, minute_step, second_step are optional step values for
         for the range of values for the associated select element
         twelve_hr: If True, forces the output to be in 12-hr format (rather than 24-hr)
         """
-        self.attrs = attrs or {}
-        
+        super(SelectTimeWidget, self).__init__(attrs)
+
         if twelve_hr:
             self.twelve_hr = True # Do 12hr (rather than 24hr)
             self.meridiem_val = 'a.m.' # Default to Morning (A.M.)
@@ -67,7 +67,7 @@ class SelectTimeWidget(Widget):
         else:
             self.minutes = range(0,60)
 
-    def render(self, name, value, attrs=None):
+    def render(self, name, value, attrs=None, renderer=None):
         try: # try to get time values from a datetime.time object (value)
             hour_val, minute_val = value.hour, value.minute
             if self.twelve_hr:
@@ -80,7 +80,7 @@ class SelectTimeWidget(Widget):
             if isinstance(value, basestring):
                 match = RE_TIME.match(value)
                 if match:
-                    time_groups = match.groups();
+                    time_groups = match.groups()
                     hour_val = int(time_groups[HOURS]) % 24 # force to range(0-24)
                     minute_val = int(time_groups[MINUTES]) 
                     
@@ -117,7 +117,7 @@ class SelectTimeWidget(Widget):
         minute_val = u"%.2d" % minute_val
 
         hour_choices = [("%.2d"%i, "%.2d"%i) for i in self.hours]
-        local_attrs = self.build_attrs(id=self.hour_field % id_)
+        local_attrs = self.build_attrs({'id': self.hour_field % id_})
         select_html = Select(choices=hour_choices).render(self.hour_field % name, hour_val, local_attrs)
         output.append(select_html)
 
@@ -169,6 +169,8 @@ class SplitSelectDateTimeWidget(MultiWidget):
     This class combines SelectTimeWidget and SelectDateWidget so we have something 
     like SpliteDateTimeWidget (in django.forms.widgets), but with Select elements.
     """
+    template_name = ''
+
     def __init__(self, attrs=None, hour_step=None, minute_step=None, twelve_hr=None, years=None):
         """ pass all these parameters to their respective widget constructors..."""
         widgets = (SelectDateWidget(attrs=attrs, years=years), SelectTimeWidget(attrs=attrs, hour_step=hour_step, minute_step=minute_step, twelve_hr=twelve_hr))
@@ -185,13 +187,6 @@ class SplitSelectDateTimeWidget(MultiWidget):
             return [value.date(), value.time().replace(microsecond=0)]
         return [None, None]
 
-    def format_output(self, rendered_widgets):
-        """
-        Given a list of rendered widgets (as strings), it inserts an HTML
-        linebreak between them.
-        
-        Returns a Unicode string representing the HTML for the whole lot.
-        """
-        rendered_widgets.insert(-1, '<br/>')
-        return u''.join(rendered_widgets)
-
+    def render(self, name, value, attrs=None, renderer=None):
+        rendered_widgets = list(widget.render(name, value, attrs=attrs, renderer=renderer) for widget in self.widgets)
+        return u'<br/>'.join(rendered_widgets)
diff --git a/helios/workflows/homomorphic.py b/helios/workflows/homomorphic.py
index 63e2d0c..c3c98eb 100644
--- a/helios/workflows/homomorphic.py
+++ b/helios/workflows/homomorphic.py
@@ -6,11 +6,7 @@ Ben Adida
 reworked 2011-01-09
 """
 
-from helios.crypto import algs, utils
-import logging
-import uuid
-import datetime
-from helios import models
+from helios.crypto import algs
 from . import WorkflowObject
 
 class EncryptedAnswer(WorkflowObject):
diff --git a/helios_auth/apps.py b/helios_auth/apps.py
new file mode 100644
index 0000000..7d1472a
--- /dev/null
+++ b/helios_auth/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+class HeliosAuthConfig(AppConfig):
+    name = 'helios_auth'
+    verbose_name = "Helios Authentication"
diff --git a/helios_auth/auth_systems/cas.py b/helios_auth/auth_systems/cas.py
index 8202ad2..dd4216b 100644
--- a/helios_auth/auth_systems/cas.py
+++ b/helios_auth/auth_systems/cas.py
@@ -5,11 +5,14 @@ Some code borrowed from
 https://sp.princeton.edu/oit/sdp/CAS/Wiki%20Pages/Python.aspx
 """
 
-from django.http import *
-from django.core.mail import send_mail
+import datetime
+import re
+import urllib
+import urllib2
+import uuid
 from django.conf import settings
-
-import sys, os, cgi, urllib, urllib2, re, uuid, datetime
+from django.core.mail import send_mail
+from django.http import HttpResponseRedirect
 from xml.etree import ElementTree
 
 CAS_EMAIL_DOMAIN = "princeton.edu"
@@ -31,11 +34,11 @@ STATUS_UPDATES = False
 
 def _get_service_url():
   # FIXME current URL
-  from helios_auth.views import after
+  from helios_auth import url_names
   from django.conf import settings
-  from django.core.urlresolvers import reverse
+  from django.urls import reverse
   
-  return settings.SECURE_URL_HOST + reverse(after)
+  return settings.SECURE_URL_HOST + reverse(url_names.AUTH_AFTER)
   
 def get_auth_url(request, redirect_url):
   request.session['cas_redirect_url'] = redirect_url
diff --git a/helios_auth/auth_systems/clever.py b/helios_auth/auth_systems/clever.py
index dcd52d0..498951f 100644
--- a/helios_auth/auth_systems/clever.py
+++ b/helios_auth/auth_systems/clever.py
@@ -3,14 +3,11 @@ Clever Authentication
 
 """
 
-from django.http import *
-from django.core.mail import send_mail
+import base64
+import httplib2
+import json
+import urllib
 from django.conf import settings
-
-import httplib2,json,base64
-
-import sys, os, cgi, urllib, urllib2, re
-
 from oauth2client.client import OAuth2WebServerFlow, OAuth2Credentials
 
 # some parameters to indicate that status updating is not possible
diff --git a/helios_auth/auth_systems/facebookclient/djangofb/default_app/urls.py b/helios_auth/auth_systems/facebookclient/djangofb/default_app/urls.py
index 5e793a6..f75d8d2 100644
--- a/helios_auth/auth_systems/facebookclient/djangofb/default_app/urls.py
+++ b/helios_auth/auth_systems/facebookclient/djangofb/default_app/urls.py
@@ -1,7 +1,7 @@
-from django.conf.urls import *
+from django.conf.urls import url
 
-urlpatterns = patterns('{{ project }}.{{ app }}.views',
-    (r'^$', 'canvas'),
-    # Define other pages you want to create here
-)
+from views import canvas
 
+urlpatterns = [
+    url(r'^$', canvas),
+]
diff --git a/helios_auth/auth_systems/google.py b/helios_auth/auth_systems/google.py
index 7caa32f..0341991 100644
--- a/helios_auth/auth_systems/google.py
+++ b/helios_auth/auth_systems/google.py
@@ -3,14 +3,10 @@ Google Authentication
 
 """
 
-from django.http import *
-from django.core.mail import send_mail
+import httplib2
+import json
 from django.conf import settings
-
-import httplib2,json
-
-import sys, os, cgi, urllib, urllib2, re
-
+from django.core.mail import send_mail
 from oauth2client.client import OAuth2WebServerFlow
 
 # some parameters to indicate that status updating is not possible
diff --git a/helios_auth/auth_systems/linkedin.py b/helios_auth/auth_systems/linkedin.py
index 32b0033..696eda9 100644
--- a/helios_auth/auth_systems/linkedin.py
+++ b/helios_auth/auth_systems/linkedin.py
@@ -4,7 +4,7 @@ LinkedIn Authentication
 
 from oauthclient import client
 
-from django.core.urlresolvers import reverse
+from django.urls import reverse
 from django.http import HttpResponseRedirect
 
 from helios_auth import utils
diff --git a/helios_auth/auth_systems/openid/util.py b/helios_auth/auth_systems/openid/util.py
index 277b92c..1ed33f3 100644
--- a/helios_auth/auth_systems/openid/util.py
+++ b/helios_auth/auth_systems/openid/util.py
@@ -10,7 +10,7 @@ from django.template.context import RequestContext
 from django.template import loader
 from django import http
 from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import reverse as reverseURL
+from django.urls import reverse as reverseURL
 
 from django.conf import settings
 
diff --git a/helios_auth/auth_systems/password.py b/helios_auth/auth_systems/password.py
index f78d9f6..aadb03f 100644
--- a/helios_auth/auth_systems/password.py
+++ b/helios_auth/auth_systems/password.py
@@ -2,17 +2,21 @@
 Username/Password Authentication
 """
 
-from django.core.urlresolvers import reverse
+from django.urls import reverse
 from django import forms
 from django.core.mail import send_mail
 from django.conf import settings
 from django.http import HttpResponseRedirect
+from django.conf.urls import url
+
+from helios_auth import url_names
 
 import logging
 
 # some parameters to indicate that status updating is possible
 STATUS_UPDATES = False
-
+PASSWORD_LOGIN_URL_NAME = "auth@password@login"
+PASSWORD_FORGOTTEN_URL_NAME = "auth@password@forgotten"
 
 def create_user(username, password, name = None):
   from helios_auth.models import User
@@ -58,7 +62,7 @@ def password_login_view(request):
         user = User.get_by_type_and_id('password', username)
         if password_check(user, password):
           request.session['password_user_id'] = user.user_id
-          return HttpResponseRedirect(reverse(after))
+          return HttpResponseRedirect(reverse(url_names.AUTH_AFTER))
       except User.DoesNotExist:
         pass
       error = 'Bad Username or Password'
@@ -101,7 +105,7 @@ Your password: %s
     return HttpResponseRedirect(return_url)
   
 def get_auth_url(request, redirect_url = None):
-  return reverse(password_login_view)
+  return reverse(PASSWORD_LOGIN_URL_NAME)
     
 def get_user_info_after_auth(request):
   from helios_auth.models import User
@@ -125,3 +129,9 @@ def send_message(user_id, user_name, user_info, subject, body):
 
 def can_create_election(user_id, user_info):
   return True
+
+
+urlpatterns = [
+  url(r'^password/login', password_login_view, name=PASSWORD_LOGIN_URL_NAME),
+  url(r'^password/forgot', password_forgotten_view, name=PASSWORD_FORGOTTEN_URL_NAME)
+]
diff --git a/helios_auth/auth_systems/twitter.py b/helios_auth/auth_systems/twitter.py
index 9963f91..8739d60 100644
--- a/helios_auth/auth_systems/twitter.py
+++ b/helios_auth/auth_systems/twitter.py
@@ -4,7 +4,8 @@ Twitter Authentication
 
 from oauthclient import client
 
-from django.core.urlresolvers import reverse
+from django.conf.urls import url
+from django.urls import reverse
 from django.http import HttpResponseRedirect
 
 from helios_auth import utils
@@ -21,6 +22,7 @@ DM_TOKEN = settings.TWITTER_DM_TOKEN
 # some parameters to indicate that status updating is possible
 STATUS_UPDATES = True
 STATUS_UPDATE_WORDING_TEMPLATE = "Tweet %s"
+FOLLOW_VIEW_URL_NAME = "auth@twitter@follow"
 
 OAUTH_PARAMS = {
   'root_url' : 'https://twitter.com',
@@ -70,7 +72,7 @@ def user_needs_intervention(user_id, user_info, token):
   if friendship:
     return None
 
-  return HttpResponseRedirect(reverse(follow_view))
+  return HttpResponseRedirect(reverse(FOLLOW_VIEW_URL_NAME))
 
 def _get_client_by_request(request):
   access_token = request.session['access_token']
@@ -114,8 +116,8 @@ def follow_view(request):
       twitter_client = _get_client_by_token(user.token)
       result = twitter_client.oauth_request('http://api.twitter.com/1/friendships/create.json', args={'screen_name': USER_TO_FOLLOW}, method='POST')
 
-    from helios_auth.views import after_intervention
-    return HttpResponseRedirect(reverse(after_intervention))
+    from helios_auth.url_names import AUTH_AFTER_INTERVENTION
+    return HttpResponseRedirect(reverse(AUTH_AFTER_INTERVENTION))
 
 
 
@@ -125,3 +127,6 @@ def follow_view(request):
 
 def can_create_election(user_id, user_info):
   return True
+
+
+urlpatterns = [url(r'^twitter/follow', follow_view, name=FOLLOW_VIEW_URL_NAME)]
\ No newline at end of file
diff --git a/helios_auth/auth_systems/yahoo.py b/helios_auth/auth_systems/yahoo.py
index 16bc034..5131a19 100644
--- a/helios_auth/auth_systems/yahoo.py
+++ b/helios_auth/auth_systems/yahoo.py
@@ -3,12 +3,8 @@ Yahoo Authentication
 
 """
 
-from django.http import *
-from django.core.mail import send_mail
 from django.conf import settings
-
-import sys, os, cgi, urllib, urllib2, re
-from xml.etree import ElementTree
+from django.core.mail import send_mail
 
 from openid import view_helpers
 
diff --git a/helios_auth/jsonfield.py b/helios_auth/jsonfield.py
index 0104ce4..34cecf7 100644
--- a/helios_auth/jsonfield.py
+++ b/helios_auth/jsonfield.py
@@ -4,11 +4,11 @@ taken from
 http://www.djangosnippets.org/snippets/377/
 """
 
-import datetime, json
-from django.db import models
-from django.db.models import signals
-from django.conf import settings
+import json
+from django.core.exceptions import ValidationError
 from django.core.serializers.json import DjangoJSONEncoder
+from django.db import models
+
 
 class JSONField(models.TextField):
     """
@@ -18,9 +18,6 @@ class JSONField(models.TextField):
     deserialization_params added on 2011-01-09 to provide additional hints at deserialization time
     """
 
-    # Used so to_python() is called
-    __metaclass__ = models.SubfieldBase
-
     def __init__(self, json_type=None, deserialization_params=None, **kwargs):
         self.json_type = json_type
         self.deserialization_params = deserialization_params
@@ -36,17 +33,21 @@ class JSONField(models.TextField):
         if isinstance(value, dict) or isinstance(value, list):
             return value
 
-        if value == "" or value == None:
+        return self.from_db_value(value)
+
+    # noinspection PyUnusedLocal
+    def from_db_value(self, value, *args, **kwargs):
+        if value == "" or value is None:
             return None
 
         try:
             parsed_value = json.loads(value)
-        except:
-            raise Exception("not JSON")
+        except Exception as e:
+            raise ValidationError("Received value is not JSON", e)
 
         if self.json_type and parsed_value:
             parsed_value = self.json_type.fromJSONDict(parsed_value, **self.deserialization_params)
-                
+
         return parsed_value
 
     # we should never look up by JSON field anyways.
@@ -57,7 +58,7 @@ class JSONField(models.TextField):
         if isinstance(value, basestring):
             return value
 
-        if value == None:
+        if value is None:
             return None
 
         if self.json_type and isinstance(value, self.json_type):
@@ -70,5 +71,4 @@ class JSONField(models.TextField):
 
     def value_to_string(self, obj):
         value = self._get_val_from_obj(obj)
-        return self.get_db_prep_value(value)        
-
+        return self.get_db_prep_value(value, None)
diff --git a/helios_auth/models.py b/helios_auth/models.py
index b917995..fb050d2 100644
--- a/helios_auth/models.py
+++ b/helios_auth/models.py
@@ -6,13 +6,11 @@ GAE
 Ben Adida
 (ben@adida.net)
 """
-
 from django.db import models
-from jsonfield import JSONField
 
-import datetime, logging
+from auth_systems import AUTH_SYSTEMS
+from jsonfield import JSONField
 
-from auth_systems import AUTH_SYSTEMS, can_check_constraint, can_list_categories
 
 # an exception to catch when a user is no longer authenticated
 class AuthenticationExpired(Exception):
@@ -35,7 +33,8 @@ class User(models.Model):
 
   class Meta:
     unique_together = (('user_type', 'user_id'),)
-    
+    app_label = 'helios_auth'
+
   @classmethod
   def _get_type_and_id(cls, user_type, user_id):
     return "%s:%s" % (user_type, user_id)    
diff --git a/helios_auth/security/__init__.py b/helios_auth/security/__init__.py
index facc0c3..d3c5ac1 100644
--- a/helios_auth/security/__init__.py
+++ b/helios_auth/security/__init__.py
@@ -4,19 +4,16 @@ Generic Security -- for the auth system
 Ben Adida (ben@adida.net)
 """
 
-# nicely update the wrapper function
-from functools import update_wrapper
-
-from django.http import HttpResponse, Http404, HttpResponseRedirect
-from django.core.exceptions import *
+import uuid
 from django.conf import settings
+from django.core.exceptions import PermissionDenied
 from django.http import HttpResponseNotAllowed
+from django.http import HttpResponseRedirect
+# nicely update the wrapper function
+from functools import update_wrapper
 
 import oauth
-
-import uuid
-
-from helios_auth.models import *
+from helios_auth.models import User
 
 FIELDS_TO_SAVE = 'FIELDS_TO_SAVE'
 
diff --git a/helios_auth/security/oauth.py b/helios_auth/security/oauth.py
index 4addf22..71676c8 100644
--- a/helios_auth/security/oauth.py
+++ b/helios_auth/security/oauth.py
@@ -6,7 +6,6 @@ Hacked a bit by Ben Adida (ben@adida.net) so that:
 - access tokens are looked up with an extra param of consumer
 """
 
-import cgi
 import urllib
 import time
 import random
@@ -55,7 +54,7 @@ class OAuthConsumer(object):
         self.secret = secret
 
 # OAuthToken is a data type that represents an End User via either an access
-# or request token.     
+# or request token.
 class OAuthToken(object):
     # access tokens and request tokens
     key = None
@@ -74,9 +73,9 @@ class OAuthToken(object):
 
     # return a token from something like:
     # oauth_token_secret=digg&oauth_token=digg
-    @staticmethod   
+    @staticmethod
     def from_string(s):
-        params = cgi.parse_qs(s, keep_blank_values=False)
+        params = urlparse.parse_qs(s, keep_blank_values=False)
         key = params['oauth_token'][0]
         secret = params['oauth_token_secret'][0]
         return OAuthToken(key, secret)
@@ -88,11 +87,11 @@ class OAuthToken(object):
 class OAuthRequest(object):
     '''
     OAuth parameters:
-        - oauth_consumer_key 
+        - oauth_consumer_key
         - oauth_token
         - oauth_signature_method
-        - oauth_signature 
-        - oauth_timestamp 
+        - oauth_signature
+        - oauth_timestamp
         - oauth_nonce
         - oauth_version
         ... any additional parameters, as defined by the Service Provider.
@@ -101,7 +100,7 @@ class OAuthRequest(object):
     http_method = HTTP_METHOD
     http_url = None
     version = VERSION
-    
+
     # added by Ben to filter out extra params from header
     OAUTH_PARAMS = ['oauth_consumer_key', 'oauth_token', 'oauth_signature_method', 'oauth_signature', 'oauth_timestamp', 'oauth_nonce', 'oauth_version']
 
@@ -173,7 +172,7 @@ class OAuthRequest(object):
         parts = urlparse.urlparse(self.http_url)
         url_string = '%s://%s%s' % (parts[0], parts[1], parts[2]) # scheme, netloc, path
         return url_string
-        
+
     # set the signature parameter to the result of build_signature
     def sign_request(self, signature_method, consumer, token):
         # set the signature method
@@ -266,11 +265,11 @@ class OAuthRequest(object):
             # remove quotes and unescape the value
             params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
         return params
-    
+
     # util function: turn url string into parameters, has to do some unescaping
     @staticmethod
     def _split_url_string(param_str):
-        parameters = cgi.parse_qs(param_str, keep_blank_values=False)
+        parameters = urlparse.parse_qs(param_str, keep_blank_values=False)
         for k, v in parameters.iteritems():
             parameters[k] = urllib.unquote(v[0])
         return parameters
@@ -287,7 +286,7 @@ class OAuthServer(object):
         self.signature_methods = signature_methods or {}
 
     def set_data_store(self, oauth_data_store):
-        self.data_store = data_store
+        self.data_store = oauth_data_store
 
     def get_data_store(self):
         return self.data_store
@@ -336,12 +335,12 @@ class OAuthServer(object):
     # authorize a request token
     def authorize_token(self, token, user):
         return self.data_store.authorize_request_token(token, user)
-    
+
     # get the callback url
     def get_callback(self, oauth_request):
         return oauth_request.get_parameter('oauth_callback')
 
-    # optional support for the authenticate header   
+    # optional support for the authenticate header
     def build_authenticate_header(self, realm=''):
         return {'WWW-Authenticate': 'OAuth realm="%s"' % realm}
 
@@ -495,7 +494,7 @@ class OAuthSignatureMethod_HMAC_SHA1(OAuthSignatureMethod):
 
     def get_name(self):
         return 'HMAC-SHA1'
-        
+
     def build_signature_base_string(self, oauth_request, consumer, token):
         sig = (
             escape(oauth_request.get_normalized_http_method()),
diff --git a/helios_auth/south_migrations/0001_initial.py b/helios_auth/south_migrations/0001_initial.py
deleted file mode 100644
index 4ba2a3a..0000000
--- a/helios_auth/south_migrations/0001_initial.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# encoding: utf-8
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-class Migration(SchemaMigration):
-
-    def forwards(self, orm):
-        
-        # Adding model 'User'
-        db.create_table('helios_auth_user', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('user_type', self.gf('django.db.models.fields.CharField')(max_length=50)),
-            ('user_id', self.gf('django.db.models.fields.CharField')(max_length=100)),
-            ('name', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)),
-            ('info', self.gf('helios_auth.jsonfield.JSONField')()),
-            ('token', self.gf('helios_auth.jsonfield.JSONField')(null=True)),
-            ('admin_p', self.gf('django.db.models.fields.BooleanField')(default=False)),
-        ))
-        db.send_create_signal('helios_auth', ['User'])
-
-        # Adding unique constraint on 'User', fields ['user_type', 'user_id']
-        db.create_unique('helios_auth_user', ['user_type', 'user_id'])
-
-
-    def backwards(self, orm):
-        
-        # Removing unique constraint on 'User', fields ['user_type', 'user_id']
-        db.delete_unique('helios_auth_user', ['user_type', 'user_id'])
-
-        # Deleting model 'User'
-        db.delete_table('helios_auth_user')
-
-
-    models = {
-        'helios_auth.user': {
-            'Meta': {'unique_together': "(('user_type', 'user_id'),)", 'object_name': 'User'},
-            'admin_p': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'info': ('helios_auth.jsonfield.JSONField', [], {}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
-            'token': ('helios_auth.jsonfield.JSONField', [], {'null': 'True'}),
-            'user_id': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'user_type': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        }
-    }
-
-    complete_apps = ['helios_auth']
diff --git a/helios_auth/south_migrations/__init__.py b/helios_auth/south_migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/helios_auth/templates/index.html b/helios_auth/templates/index.html
index f16a7b8..3c2e9f8 100644
--- a/helios_auth/templates/index.html
+++ b/helios_auth/templates/index.html
@@ -8,7 +8,7 @@
     You are currently logged in as<br /><b>{{user.user_id}}</b> via <b>{{user.user_type}}</b>.
 </p>
 <p>
-    <a href="{% url "helios_auth.views.logout" %}">logout</a>
+    <a href="{% url "auth@logout" %}">logout</a>
 </p>
 
 {% else %}
diff --git a/helios_auth/templates/login_box.html b/helios_auth/templates/login_box.html
index 2e89e94..27ca7da 100644
--- a/helios_auth/templates/login_box.html
+++ b/helios_auth/templates/login_box.html
@@ -1,12 +1,12 @@
 {% if default_auth_system %}
 <p>
-<a class="small button" href="{% url "helios_auth.views.start" system_name=default_auth_system %}?return_url={{return_url}}">Log in</a></p>
+<a class="small button" href="{% url "auth@start" system_name=default_auth_system %}?return_url={{return_url}}">Log in</a></p>
 {% else %}
 {% for auth_system in enabled_auth_systems %}
 {% ifequal auth_system "password" %}
 {% else %}
 <p>
-    <a href="{{SECURE_URL_HOST}}{% url "helios_auth.views.start" system_name=auth_system %}?return_url={{return_url}}" style="font-size: 1.4em;">
+    <a href="{{SECURE_URL_HOST}}{% url "auth@start" system_name=auth_system %}?return_url={{return_url}}" style="font-size: 1.4em;">
 <img style="height: 35px; border: 0px;" src="/static/auth/login-icons/{{auth_system}}.png" alt="{{auth_system}}" /> {{auth_system}}
 {% endifequal %}
 </a>
diff --git a/helios_auth/templates/perms_why.html b/helios_auth/templates/perms_why.html
index 179d103..3f38fd1 100644
--- a/helios_auth/templates/perms_why.html
+++ b/helios_auth/templates/perms_why.html
@@ -13,7 +13,7 @@ this information, we unfortunately cannot help you vote.
 </p>
 
 <form method="POST" action="">
-<input type="hidden" value="{{csrf_token}}" />
+<input type="hidden" name="csrf_token" value="{{csrf_token}}" />
 <input type="submit" value="OK, I understand, let's do this login thing." /><br /><br />
 <a href="http://google.com">nope, get me out of here</a>.
 </form>
diff --git a/helios_auth/tests.py b/helios_auth/tests.py
index f07f309..de21e3b 100644
--- a/helios_auth/tests.py
+++ b/helios_auth/tests.py
@@ -91,7 +91,7 @@ class UserModelTests(unittest.TestCase):
 
 import views
 import auth_systems.password as password_views
-from django.core.urlresolvers import reverse
+from django.urls import reverse
 
 # FIXME: login CSRF should make these tests more complicated
 # and should be tested for
@@ -116,7 +116,7 @@ class UserBlackboxTests(TestCase):
         # self.assertContains(response, "Foobar User")
 
     def test_logout(self):
-        response = self.client.post(reverse(views.logout), follow=True)
+        response = self.client.post(reverse("auth@logout"), follow=True)
         
         self.assertContains(response, "not logged in")
         self.assertNotContains(response, "Foobar User")
diff --git a/helios_auth/url_names.py b/helios_auth/url_names.py
new file mode 100644
index 0000000..29f7b22
--- /dev/null
+++ b/helios_auth/url_names.py
@@ -0,0 +1,6 @@
+AUTH_INDEX="auth@index"
+AUTH_LOGOUT="auth@logout"
+AUTH_START="auth@start"
+AUTH_AFTER="auth@after"
+AUTH_WHY="auth@why"
+AUTH_AFTER_INTERVENTION="auth@after-intervention"
diff --git a/helios_auth/urls.py b/helios_auth/urls.py
index 11d1013..5244e10 100644
--- a/helios_auth/urls.py
+++ b/helios_auth/urls.py
@@ -7,27 +7,27 @@ Ben Adida (ben@adida.net)
 
 from django.conf.urls import url
 
+import url_names
 import views
 from settings import AUTH_ENABLED_AUTH_SYSTEMS
 
 urlpatterns = [
     # basic static stuff
-    url(r'^$', views.index),
-    url(r'^logout$', views.logout),
-    url(r'^start/(?P<system_name>.*)$', views.start),
+    url(r'^$', views.index, name=url_names.AUTH_INDEX),
+    url(r'^logout$', views.logout, name=url_names.AUTH_LOGOUT),
+    url(r'^start/(?P<system_name>.*)$', views.start, name=url_names.AUTH_START),
     # weird facebook constraint for trailing slash
-    url(r'^after/$', views.after),
-    url(r'^why$', views.perms_why),
-    url(r'^after_intervention$', views.after_intervention),
+    url(r'^after/$', views.after, name=url_names.AUTH_AFTER),
+    url(r'^why$', views.perms_why, name=url_names.AUTH_WHY),
+    url(r'^after_intervention$', views.after_intervention, name=url_names.AUTH_AFTER_INTERVENTION),
 ]
 
 # password auth
 if 'password' in AUTH_ENABLED_AUTH_SYSTEMS:
-    from auth_systems.password import password_login_view, password_forgotten_view
-    urlpatterns.append(url(r'^password/login', password_login_view))
-    urlpatterns.append(url(r'^password/forgot', password_forgotten_view))
+    from auth_systems.password import urlpatterns as password_patterns
+    urlpatterns.extend(password_patterns)
 
 # twitter
 if 'twitter' in AUTH_ENABLED_AUTH_SYSTEMS:
-    from auth_systems.twitter import follow_view
-    urlpatterns.append(url(r'^twitter/follow', follow_view))
+    from auth_systems.twitter import urlpatterns as twitter_patterns
+    urlpatterns.extend(twitter_patterns)
diff --git a/helios_auth/view_utils.py b/helios_auth/view_utils.py
index 1355ba4..6699ab2 100644
--- a/helios_auth/view_utils.py
+++ b/helios_auth/view_utils.py
@@ -4,15 +4,13 @@ Utilities for all views
 Ben Adida (12-30-2008)
 """
 
-from django.template import Context, Template, loader
-from django.http import HttpResponse, Http404
+from django.conf import settings
+from django.http import HttpResponse
 from django.shortcuts import render_to_response
-
-from helios_auth.security import get_user
+from django.template import loader
 
 import helios_auth
-
-from django.conf import settings
+from helios_auth.security import get_user
 
 ##
 ## BASICS
@@ -24,37 +22,37 @@ SUCCESS = HttpResponse("SUCCESS")
 ## template abstraction
 ##
 
-def prepare_vars(request, vars):
-  vars_with_user = vars.copy()
-  
+def prepare_vars(request, values):
+  vars_with_user = values.copy()
+
   if request:
     vars_with_user['user'] = get_user(request)
     vars_with_user['csrf_token'] = request.session['csrf_token']
     vars_with_user['SECURE_URL_HOST'] = settings.SECURE_URL_HOST
-    
+
   vars_with_user['STATIC'] = '/static/auth'
   vars_with_user['MEDIA_URL'] = '/static/auth/'
   vars_with_user['TEMPLATE_BASE'] = helios_auth.TEMPLATE_BASE
-  
+
   vars_with_user['settings'] = settings
-  
+
   return vars_with_user
-  
-def render_template(request, template_name, vars = {}):
-  t = loader.get_template(template_name + '.html')
-  
-  vars_with_user = prepare_vars(request, vars)
-  
+
+
+def render_template(request, template_name, values=None):
+  vars_with_user = prepare_vars(request, values or {})
+
   return render_to_response('helios_auth/templates/%s.html' % template_name, vars_with_user)
 
-def render_template_raw(request, template_name, vars={}):
+
+def render_template_raw(request, template_name, values=None):
   t = loader.get_template(template_name + '.html')
-  
-  vars_with_user = prepare_vars(request, vars)
-  c = Context(vars_with_user)  
-  return t.render(c)
+  values = values or {}
 
-def render_json(json_txt):
-  return HttpResponse(json_txt)
+  vars_with_user = prepare_vars(request, values)
+
+  return t.render(context=vars_with_user, request=request)
 
 
+def render_json(json_txt):
+  return HttpResponse(json_txt)
diff --git a/helios_auth/views.py b/helios_auth/views.py
index b85f82e..f246daf 100644
--- a/helios_auth/views.py
+++ b/helios_auth/views.py
@@ -5,22 +5,20 @@ Ben Adida
 2009-07-05
 """
 
-from django.http import *
-from django.core.urlresolvers import reverse
+import urllib
+from django.urls import reverse
+from django.http import HttpResponseRedirect, HttpResponse
 
-from view_utils import *
-from helios_auth.security import get_user
-
-import auth_systems
+import helios_auth
+import settings
 from auth_systems import AUTH_SYSTEMS
 from auth_systems import password
-import helios_auth
-
-import copy, urllib
-
+from helios_auth.security import get_user
+from helios_auth.url_names import AUTH_INDEX, AUTH_START, AUTH_AFTER, AUTH_WHY, AUTH_AFTER_INTERVENTION
 from models import User
-
 from security import FIELDS_TO_SAVE
+from view_utils import render_template, render_template_raw
+
 
 def index(request):
   """
@@ -31,7 +29,7 @@ def index(request):
 
   # single auth system?
   if len(helios_auth.ENABLED_AUTH_SYSTEMS) == 1 and not user:
-    return HttpResponseRedirect(reverse(start, args=[helios_auth.ENABLED_AUTH_SYSTEMS[0]])+ '?return_url=' + request.GET.get('return_url', ''))
+    return HttpResponseRedirect(reverse(AUTH_START, args=[helios_auth.ENABLED_AUTH_SYSTEMS[0]])+ '?return_url=' + request.GET.get('return_url', ''))
 
   #if helios_auth.DEFAULT_AUTH_SYSTEM and not user:
   #  return HttpResponseRedirect(reverse(start, args=[helios_auth.DEFAULT_AUTH_SYSTEM])+ '?return_url=' + request.GET.get('return_url', ''))
@@ -42,10 +40,10 @@ def index(request):
 
   #form = password.LoginForm()
 
-  return render_template(request,'index', {'return_url' : request.GET.get('return_url', '/'),
-                                           'enabled_auth_systems' : helios_auth.ENABLED_AUTH_SYSTEMS,
-                                           'default_auth_system': helios_auth.DEFAULT_AUTH_SYSTEM,
-                                           'default_auth_system_obj': default_auth_system_obj})
+  return render_template(request, 'index', {'return_url' : request.GET.get('return_url', '/'),
+                                            'enabled_auth_systems' : helios_auth.ENABLED_AUTH_SYSTEMS,
+                                            'default_auth_system': helios_auth.DEFAULT_AUTH_SYSTEM,
+                                            'default_auth_system_obj': default_auth_system_obj})
 
 def login_box_raw(request, return_url='/', auth_systems = None):
   """
@@ -56,7 +54,7 @@ def login_box_raw(request, return_url='/', auth_systems = None):
     default_auth_system_obj = AUTH_SYSTEMS[helios_auth.DEFAULT_AUTH_SYSTEM]
 
   # make sure that auth_systems includes only available and enabled auth systems
-  if auth_systems != None:
+  if auth_systems is not None:
     enabled_auth_systems = set(auth_systems).intersection(set(helios_auth.ENABLED_AUTH_SYSTEMS)).intersection(set(AUTH_SYSTEMS.keys()))
   else:
     enabled_auth_systems = set(helios_auth.ENABLED_AUTH_SYSTEMS).intersection(set(AUTH_SYSTEMS.keys()))
@@ -87,7 +85,7 @@ def do_local_logout(request):
 
   # let's clean up the self-referential issue:
   field_names_to_save = set(field_names_to_save)
-  field_names_to_save = field_names_to_save - set([FIELDS_TO_SAVE])
+  field_names_to_save = field_names_to_save - {FIELDS_TO_SAVE}
   field_names_to_save = list(field_names_to_save)
 
   fields_to_save = dict([(name, request.session.get(name, None)) for name in field_names_to_save])
@@ -144,7 +142,7 @@ def _do_auth(request):
   system = AUTH_SYSTEMS[system_name]
   
   # where to send the user to?
-  redirect_url = "%s%s" % (settings.SECURE_URL_HOST,reverse(after))
+  redirect_url = settings.SECURE_URL_HOST + reverse(AUTH_AFTER)
   auth_url = system.get_auth_url(request, redirect_url=redirect_url)
   
   if auth_url:
@@ -154,7 +152,7 @@ def _do_auth(request):
   
 def start(request, system_name):
   if not (system_name in helios_auth.ENABLED_AUTH_SYSTEMS):
-    return HttpResponseRedirect(reverse(index))
+    return HttpResponseRedirect(reverse(AUTH_INDEX))
   
   # why is this here? Let's try without it
   # request.session.save()
@@ -190,7 +188,7 @@ def after(request):
     
     request.session['user'] = user
   else:
-    return HttpResponseRedirect("%s?%s" % (reverse(perms_why), urllib.urlencode({'system_name' : request.session['auth_system_name']})))
+    return HttpResponseRedirect("%s?%s" % (reverse(AUTH_WHY), urllib.urlencode({'system_name' : request.session['auth_system_name']})))
 
   # does the auth system want to present an additional view?
   # this is, for example, to prompt the user to follow @heliosvoting
@@ -201,12 +199,12 @@ def after(request):
       return intervention_response
 
   # go to the after intervention page. This is for modularity
-  return HttpResponseRedirect(reverse(after_intervention))
+  return HttpResponseRedirect(reverse(AUTH_AFTER_INTERVENTION))
 
 def after_intervention(request):
   return_url = "/"
   if request.session.has_key('auth_return_url'):
     return_url = request.session['auth_return_url']
     del request.session['auth_return_url']
-  return HttpResponseRedirect("%s%s" % (settings.URL_HOST, return_url))
+  return HttpResponseRedirect(settings.URL_HOST + return_url)
 
diff --git a/requirements.txt b/requirements.txt
index 237e75c..8e69fb2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,9 +1,8 @@
-Django==1.8.19
+Django==1.11.28
 anyjson==0.3.3
-celery==3.1.18
-django-celery==3.1.16
+celery==4.2.1
 django-picklefield==0.3.0
-kombu==3.0.30
+kombu==4.2.0
 html5lib==0.999
 psycopg2==2.7.3.2
 pyparsing==1.5.7
@@ -14,10 +13,8 @@ gunicorn==19.9
 requests==2.21.0
 unicodecsv==0.9.0
 dj_database_url==0.3.0
-django-sslify==0.2.7
 django_webtest>=1.9
 webtest==2.0.18
-django-secure==1.0.1
 bleach==1.4.1
 boto==2.27.0
 django-ses==0.6.0
diff --git a/reset.sh b/reset.sh
index 52141e1..8192723 100755
--- a/reset.sh
+++ b/reset.sh
@@ -2,6 +2,6 @@
 set -e  # Exit immediately if a command exits with a non-zero status.
 dropdb helios
 createdb helios
-python manage.py syncdb
+python manage.py makemigrations
 python manage.py migrate
 echo "from helios_auth.models import User; User.objects.create(user_type='google',user_id='ben@adida.net', info={'name':'Ben Adida'})" | python manage.py shell
\ No newline at end of file
diff --git a/server_ui/__init__.py b/server_ui/__init__.py
index bb89b65..3f1966c 100644
--- a/server_ui/__init__.py
+++ b/server_ui/__init__.py
@@ -1,5 +1,3 @@
 """
 This django app is meant only to connect the pieces of Helios and Auth to present a clean UI
 """
-
-import glue
diff --git a/server_ui/glue.py b/server_ui/glue.py
index 9b3b5b3..12b64b1 100644
--- a/server_ui/glue.py
+++ b/server_ui/glue.py
@@ -2,36 +2,35 @@
 Glue some events together 
 """
 
-from django.conf import settings
-from django.core.urlresolvers import reverse
-from django.conf import settings
+import helios.signals
+import helios.views
 from helios.view_utils import render_template_raw
-import helios.views, helios.signals
 
-import views
 
 def vote_cast_send_message(user, voter, election, cast_vote, **kwargs):
-  ## FIXME: this doesn't work for voters that are not also users
-  # prepare the message
-  subject_template = 'email/cast_vote_subject.txt'
-  body_template = 'email/cast_vote_body.txt'
-  
-  extra_vars = {
-    'election' : election,
-    'voter': voter,
-    'cast_vote': cast_vote,
-    'cast_vote_url': helios.views.get_castvote_url(cast_vote),
-    'custom_subject' : "%s - vote cast" % election.name
-  }
-  subject = render_template_raw(None, subject_template, extra_vars)
-  body = render_template_raw(None, body_template, extra_vars)
-  
-  # send it via the notification system associated with the auth system
-  user.send_message(subject, body)
-
-helios.signals.vote_cast.connect(vote_cast_send_message)
+    ## FIXME: this doesn't work for voters that are not also users
+    # prepare the message
+    subject_template = 'email/cast_vote_subject.txt'
+    body_template = 'email/cast_vote_body.txt'
+
+    extra_vars = {
+        'election': election,
+        'voter': voter,
+        'cast_vote': cast_vote,
+        'cast_vote_url': helios.views.get_castvote_url(cast_vote),
+        'custom_subject': "%s - vote cast" % election.name
+    }
+    subject = render_template_raw(None, subject_template, extra_vars)
+    body = render_template_raw(None, body_template, extra_vars)
+
+    # send it via the notification system associated with the auth system
+    user.send_message(subject, body)
+
 
 def election_tallied(election, **kwargs):
-  pass
+    pass
+
 
-helios.signals.election_tallied.connect(election_tallied)
+def glue():
+    helios.signals.vote_cast.connect(vote_cast_send_message)
+    helios.signals.election_tallied.connect(election_tallied)
diff --git a/server_ui/templates/base.html b/server_ui/templates/base.html
index ac5f2d1..8357be2 100644
--- a/server_ui/templates/base.html
+++ b/server_ui/templates/base.html
@@ -42,7 +42,7 @@
       <!-- Right Nav Section -->
       <ul class="right">
 	{% if user and user.admin_p %}
-	<li><a href="{% url "helios.stats_views.home" %}">Admin</a></li>
+	<li><a href="{% url "stats@home" %}">Admin</a></li>
 	<li class="divider"></li>
 	{% endif %}
 	{% if not settings.MASTER_HELIOS %}
@@ -88,13 +88,13 @@
     </span>-->
     {% if user %}
     logged in as <b>{{user.display_html_small|safe}}</b>&nbsp;&nbsp;
-    <a class="tiny button" href="{% url "helios_auth.views.logout" %}?return_url={{CURRENT_URL}}">logout</a><br />
+    <a class="tiny button" href="{% url "auth@logout" %}?return_url={{CURRENT_URL}}">logout</a><br />
     {% else %}
     {% if voter %}
-    You are signed in as voter <u>{% if voter.alias %}{{voter.alias}}{% else %}{{voter.name}}{% endif %}</u> in election <u>{{voter.election.name}}</u>. [<a href="{{settings.SECURE_URL_HOST}}{% url "helios_auth.views.logout" %}?return_url={{CURRENT_URL}}">sign out</a>]
+    You are signed in as voter <u>{% if voter.alias %}{{voter.alias}}{% else %}{{voter.name}}{% endif %}</u> in election <u>{{voter.election.name}}</u>. [<a href="{{settings.SECURE_URL_HOST}}{% url "auth@logout" %}?return_url={{CURRENT_URL}}">sign out</a>]
     {% else %}
 {% if settings.SHOW_LOGIN_OPTIONS %}
-    not logged in. <a class="tiny button" href="{{settings.SECURE_URL_HOST}}{% url "helios_auth.views.index" %}?return_url={{CURRENT_URL}}">log in</a>
+    not logged in. <a class="tiny button" href="{{settings.SECURE_URL_HOST}}{% url "auth@index" %}?return_url={{CURRENT_URL}}">log in</a>
 {% else %}
 powered by <a href="http://heliosvoting.org">Helios Voting</a>.
 {% endif %}
diff --git a/server_ui/templates/confirm.html b/server_ui/templates/confirm.html
index d98f65f..2f24a80 100644
--- a/server_ui/templates/confirm.html
+++ b/server_ui/templates/confirm.html
@@ -38,7 +38,7 @@ function show_waiting() {
 </form>
 
 <p>
-    Forgot your password? <a href="{% url "helios_auth.auth_systems.password.password_forgotten_view" %}?return_url={% url "server_ui.views.cast_confirm" %}">Have it emailed to you</a>.<br />(don't worry, we won't forget your vote).
+    Forgot your password? <a href="{% url "auth@password@forgotten" %}?return_url={% url "election@cast-confirm" %}">Have it emailed to you</a>.<br />(don't worry, we won't forget your vote).
 </p>
 </div>
 
diff --git a/server_ui/templates/done.html b/server_ui/templates/done.html
index 47ff73f..d428106 100644
--- a/server_ui/templates/done.html
+++ b/server_ui/templates/done.html
@@ -11,7 +11,7 @@
 </p>
 
 <p>
-    Visit the <a href="{% url "helios.views.one_election_view" election.uuid %}">election homepage</a>.
+    Visit the <a href="{% url "election@view" election.uuid %}">election homepage</a>.
 </p>
 
 {% endblock %}
diff --git a/server_ui/templates/election_tallied.html b/server_ui/templates/election_tallied.html
index 747e083..42d4441 100644
--- a/server_ui/templates/election_tallied.html
+++ b/server_ui/templates/election_tallied.html
@@ -9,6 +9,6 @@
   </p>
   
   <p>
-      <a href="{% url "helios.views.one_election_view" election.uuid %}">view the election</a>
+      <a href="{% url "election@view" election.uuid %}">view the election</a>
   </p>
 {% endblock %}
\ No newline at end of file
diff --git a/server_ui/templates/index.html b/server_ui/templates/index.html
index e31ac19..4b1fcec 100644
--- a/server_ui/templates/index.html
+++ b/server_ui/templates/index.html
@@ -28,7 +28,7 @@ More than <b>2,000,000 votes</b> have been cast using Helios.
 </p>
 
 {% if create_p %}
-<a class="button" href="{% url "helios.views.election_new" %}">create an election</a>
+<a class="button" href="{% url "elections@new" %}">create an election</a>
 {% endif %}
 
   {% else %}
@@ -40,7 +40,7 @@ More than <b>2,000,000 votes</b> have been cast using Helios.
   <p>
     {% for election in elections %}
     <div class="panel">
-      <a style="font-size: 1.4em;" href="{% url "helios.views.election_shortcut" election.short_name %}">{{election.name}}</a>{% if settings.SHOW_USER_INFO %}<br /> by {{election.admin.display_html_small|safe}}{% endif %}
+      <a style="font-size: 1.4em;" href="{% url "shortcut@election" election.short_name %}">{{election.name}}</a>{% if settings.SHOW_USER_INFO %}<br /> by {{election.admin.display_html_small|safe}}{% endif %}
     </div>
     <br />
     {% endfor %}
@@ -58,19 +58,19 @@ More than <b>2,000,000 votes</b> have been cast using Helios.
 {% if user %}
 <!--<div class="row right">{{user.display_html_big|safe}}</div>-->
 {% if create_p %}
-<a class="small button" href="{% url "helios.views.election_new" %}">create election</a>
+<a class="small button" href="{% url "elections@new" %}">create election</a>
 <h5 class="subheader">Administration</h5>
 {% if elections_administered %}
 <ul>
 {% for election in elections_administered %}
-<li> <a href="{% url "helios.views.one_election_view" election.uuid %}">{{election.name}}</a></li>
+<li> <a href="{% url "election@view" election.uuid %}">{{election.name}}</a></li>
 {% endfor %}
 </ul>
 {% else %}
 <em>none yet</em>
 {% endif %}
 <div class="row right">
-<a class="tiny button" href="{% url "helios.views.elections_administered" %}">see all</a>
+<a class="tiny button" href="{% url "elections@administered" %}">see all</a>
 </div>
 <div class="row"></div>
 {% endif %}
@@ -79,13 +79,13 @@ More than <b>2,000,000 votes</b> have been cast using Helios.
 {% if elections_voted %}
 <ul>
 {% for election in elections_voted %}
-<li><a href="{% url "helios.views.one_election_view" election.uuid %}">{{election.name}}</a></li>
+<li><a href="{% url "election@view" election.uuid %}">{{election.name}}</a></li>
 {% endfor %}
 </ul>
 {% else %}
 <em>none yet</em>
 {% endif %}
-<div class="row right"><a class="tiny button" href="{% url "helios.views.elections_voted" %}">see all</a></div>
+<div class="row right"><a class="tiny button" href="{% url "elections@voted" %}">see all</a></div>
 <div class="row"></div>
 {% else %}
 {% if settings.SHOW_LOGIN_OPTIONS %}
diff --git a/server_ui/urls.py b/server_ui/urls.py
index 0711bd7..e691c07 100644
--- a/server_ui/urls.py
+++ b/server_ui/urls.py
@@ -1,12 +1,12 @@
 # -*- coding: utf-8 -*-
-from django.conf.urls import patterns
+from django.conf.urls import url
 
 from views import home, about, docs, faq, privacy
 
-urlpatterns = patterns('',
-  (r'^$', home),
-  (r'^about$', about),
-  (r'^docs$', docs),
-  (r'^faq$', faq),
-  (r'^privacy$', privacy),
-)
+urlpatterns = [
+  url(r'^$', home),
+  url(r'^about$', about),
+  url(r'^docs$', docs),
+  url(r'^faq$', faq),
+  url(r'^privacy$', privacy),
+]
diff --git a/server_ui/view_utils.py b/server_ui/view_utils.py
index e22fcd5..df94721 100644
--- a/server_ui/view_utils.py
+++ b/server_ui/view_utils.py
@@ -4,21 +4,17 @@ Utilities for single election views
 Ben Adida (2009-07-18)
 """
 
-from django.template import Context, Template, loader
-from django.http import HttpResponse, Http404
+from django.conf import settings
 from django.shortcuts import render_to_response
 
 from helios_auth.security import get_user
 
-from django.conf import settings
 
 ##
 ## template abstraction
 ##
-def render_template(request, template_name, vars = {}):
-  t = loader.get_template(template_name + '.html')
-  
-  vars_with_user = vars.copy()
+def render_template(request, template_name, values = None):
+  vars_with_user = values.copy() if values is not None else {}
   vars_with_user['user'] = get_user(request)
   vars_with_user['settings'] = settings
   vars_with_user['CURRENT_URL'] = request.path
diff --git a/server_ui/views.py b/server_ui/views.py
index a4fe095..190d902 100644
--- a/server_ui/views.py
+++ b/server_ui/views.py
@@ -2,23 +2,19 @@
 server_ui specific views
 """
 
-from helios.models import *
-from helios_auth.security import *
-from view_utils import *
+import copy
+from django.conf import settings
 
-import helios.views
-import helios
-from helios.crypto import utils as cryptoutils
-from helios_auth.security import *
+import helios_auth.views as auth_views
+from helios.models import Election
 from helios.security import can_create_election
+from helios_auth.security import get_user
+from view_utils import render_template
+import glue
 
-from django.core.urlresolvers import reverse
-from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponseNotAllowed
 
-from django.conf import settings
+glue.glue()  # actually apply glue helios.view <-> helios.signals
 
-import copy
-import helios_auth.views as auth_views
 
 def get_election():
   return None
diff --git a/settings.py b/settings.py
index 2cf2d8f..1a4a08a 100644
--- a/settings.py
+++ b/settings.py
@@ -1,8 +1,10 @@
 
-import os, json
-
 # a massive hack to see if we're testing, in which case we use different settings
 import sys
+
+import json
+import os
+
 TESTING = 'test' in sys.argv
 
 # go through environment variables and override them
@@ -13,7 +15,6 @@ def get_from_env(var, default):
         return default
 
 DEBUG = (get_from_env('DEBUG', '1') == '1')
-TEMPLATE_DEBUG = DEBUG
 
 # add admins of the form: 
 #    ('Ben Adida', 'ben@adida.net'),
@@ -40,14 +41,12 @@ DATABASES = {
     }
 }
 
-SOUTH_DATABASE_ADAPTERS = {'default':'south.db.postgresql_psycopg2'}
-
 # override if we have an env variable
 if get_from_env('DATABASE_URL', None):
     import dj_database_url
     DATABASES['default'] =  dj_database_url.config()
     DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql_psycopg2'
-    DATABASES['default']['CONN_MAX_AGE'] = 600
+    DATABASES['default']['CONN_MAX_AGE'] = '600'
 
     # require SSL
     DATABASES['default']['OPTIONS'] = {'sslmode': 'require'}
@@ -93,7 +92,7 @@ SECRET_KEY = get_from_env('SECRET_KEY', 'replaceme')
 ALLOWED_HOSTS = get_from_env('ALLOWED_HOSTS', 'localhost').split(",")
 
 # Secure Stuff
-if (get_from_env('SSL', '0') == '1'):
+if get_from_env('SSL', '0') == '1':
     SECURE_SSL_REDIRECT = True
     SESSION_COOKIE_SECURE = True
 
@@ -104,7 +103,7 @@ SESSION_COOKIE_HTTPONLY = True
 
 # let's go with one year because that's the way to do it now
 STS = False
-if (get_from_env('HSTS', '0') == '1'):
+if get_from_env('HSTS', '0') == '1':
     STS = True
     # we're using our own custom middleware now
     # SECURE_HSTS_SECONDS = 31536000
@@ -114,45 +113,46 @@ if (get_from_env('HSTS', '0') == '1'):
 SECURE_BROWSER_XSS_FILTER = True
 SECURE_CONTENT_TYPE_NOSNIFF = True
 
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
-    'django.template.loaders.filesystem.Loader',
-    'django.template.loaders.app_directories.Loader'
-)
-
-MIDDLEWARE_CLASSES = (
-    # make all things SSL
-    #'sslify.middleware.SSLifyMiddleware',
+SILENCED_SYSTEM_CHECKS = ['urls.W002']
 
+MIDDLEWARE = [
     # secure a bunch of things
-    'djangosecure.middleware.SecurityMiddleware',
+    'django.middleware.security.SecurityMiddleware',
     'helios.security.HSTSMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
+    # 'django.middleware.csrf.CsrfViewMiddleware',
 
     'django.middleware.common.CommonMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
-    'django.contrib.auth.middleware.AuthenticationMiddleware'
-)
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+]
 
 ROOT_URLCONF = 'urls'
 
 ROOT_PATH = os.path.dirname(__file__)
-TEMPLATE_DIRS = (
-    ROOT_PATH,
-    os.path.join(ROOT_PATH, 'templates')
-)
+
+TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'APP_DIRS': True,
+        'DIRS': [
+            ROOT_PATH,
+            os.path.join(ROOT_PATH, 'templates'),
+            # os.path.join(ROOT_PATH, 'helios/templates'),  # covered by APP_DIRS:True
+            # os.path.join(ROOT_PATH, 'helios_auth/templates'),  # covered by APP_DIRS:True
+            # os.path.join(ROOT_PATH, 'server_ui/templates'),  # covered by APP_DIRS:True
+        ],
+        'OPTIONS': {
+            'debug': DEBUG
+        }
+    },
+]
 
 INSTALLED_APPS = (
-#    'django.contrib.auth',
-#    'django.contrib.contenttypes',
-    'djangosecure',
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
     'django.contrib.sessions',
-    #'django.contrib.sites',
-    ## needed for queues
-    'djcelery',
-    'kombu.transport.django',
-    ## in Django 1.7 we now use built-in migrations, no more south
-    ## 'south',
+    'django.contrib.sites',
     ## HELIOS stuff
     'helios_auth',
     'helios',
@@ -267,23 +267,17 @@ logging.basicConfig(
 )
 
 
-# set up django-celery
-# BROKER_BACKEND = "kombu.transport.DatabaseTransport"
-BROKER_URL = "django://"
-CELERY_RESULT_DBURI = DATABASES['default']
-import djcelery
-djcelery.setup_loader()
-
-
-# for testing
-TEST_RUNNER = 'djcelery.contrib.test_runner.CeleryTestSuiteRunner'
-# this effectively does CELERY_ALWAYS_EAGER = True
+# set up celery
+CELERY_BROKER_URL = get_from_env('CELERY_BROKER_URL', 'amqp://localhost')
+if TESTING:
+    CELERY_TASK_ALWAYS_EAGER = True
+#database_url = DATABASES['default']
 
 # Rollbar Error Logging
 ROLLBAR_ACCESS_TOKEN = get_from_env('ROLLBAR_ACCESS_TOKEN', None)
 if ROLLBAR_ACCESS_TOKEN:
   print "setting up rollbar"
-  MIDDLEWARE_CLASSES += ('rollbar.contrib.django.middleware.RollbarNotifierMiddleware',)
+  MIDDLEWARE += ['rollbar.contrib.django.middleware.RollbarNotifierMiddleware',]
   ROLLBAR = {
     'access_token': ROLLBAR_ACCESS_TOKEN,
     'environment': 'development' if DEBUG else 'production',  
diff --git a/urls.py b/urls.py
index d467499..8c29e83 100644
--- a/urls.py
+++ b/urls.py
@@ -1,21 +1,19 @@
 # -*- coding: utf-8 -*-
-from django.conf.urls import *
-from django.contrib import admin
 from django.conf import settings
+from django.conf.urls import url, include
+from django.views.static import serve
 
-urlpatterns = patterns(
-    '',
-    (r'^auth/', include('helios_auth.urls')),
-    (r'^helios/', include('helios.urls')),
+urlpatterns = [
+    url(r'^auth/', include('helios_auth.urls')),
+    url(r'^helios/', include('helios.urls')),
 
     # SHOULD BE REPLACED BY APACHE STATIC PATH
-    (r'booth/(?P<path>.*)$', 'django.views.static.serve', {'document_root' : settings.ROOT_PATH + '/heliosbooth'}),
-    (r'verifier/(?P<path>.*)$', 'django.views.static.serve', {'document_root' : settings.ROOT_PATH + '/heliosverifier'}),
+    url(r'booth/(?P<path>.*)$', serve, {'document_root' : settings.ROOT_PATH + '/heliosbooth'}),
+    url(r'verifier/(?P<path>.*)$', serve, {'document_root' : settings.ROOT_PATH + '/heliosverifier'}),
 
-    (r'static/auth/(?P<path>.*)$', 'django.views.static.serve', {'document_root' : settings.ROOT_PATH + '/helios_auth/media'}),
-    (r'static/helios/(?P<path>.*)$', 'django.views.static.serve', {'document_root' : settings.ROOT_PATH + '/helios/media'}),
-    (r'static/(?P<path>.*)$', 'django.views.static.serve', {'document_root' : settings.ROOT_PATH + '/server_ui/media'}),
+    url(r'static/auth/(?P<path>.*)$', serve, {'document_root' : settings.ROOT_PATH + '/helios_auth/media'}),
+    url(r'static/helios/(?P<path>.*)$', serve, {'document_root' : settings.ROOT_PATH + '/helios/media'}),
+    url(r'static/(?P<path>.*)$', serve, {'document_root' : settings.ROOT_PATH + '/server_ui/media'}),
 
-    (r'^', include('server_ui.urls')),
-
-    )
+    url(r'^', include('server_ui.urls')),
+]
-- 
GitLab