diff --git a/.gitignore b/.gitignore
index 8c060f8e441a21ac91ce0010886a15095dd369e9..5c112ccaae9b09a9f9313a172110e87f808dfc48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,5 @@ deploy-latest.sh
 *~
 media/*
 venv
-celerybeat-*
\ No newline at end of file
+celerybeat-*
+env.sh
\ No newline at end of file
diff --git a/helios/security.py b/helios/security.py
index 1a5a78df6e1ae875f2e0dd5b7379658dac8e3c52..971499e7c18fe50e268591a39aea9cf0cacc61f5 100644
--- a/helios/security.py
+++ b/helios/security.py
@@ -187,7 +187,7 @@ def can_create_election(request):
   if helios.ADMIN_ONLY:
     return user.admin_p
   else:
-    return user != None
+    return user.can_create_election()
   
 def user_can_feature_election(user, election):
   if not user:
diff --git a/helios_auth/auth_systems/__init__.py b/helios_auth/auth_systems/__init__.py
index 202fe95c079bc270639b57941e33f72f53078990..5a0e9233ba4df89067688283e90649aef4f1ae70 100644
--- a/helios_auth/auth_systems/__init__.py
+++ b/helios_auth/auth_systems/__init__.py
@@ -1,7 +1,7 @@
 
 AUTH_SYSTEMS = {}
 
-import twitter, password, cas, facebook, google, yahoo, linkedin
+import twitter, password, cas, facebook, google, yahoo, linkedin, clever
 AUTH_SYSTEMS['twitter'] = twitter
 AUTH_SYSTEMS['linkedin'] = linkedin
 AUTH_SYSTEMS['password'] = password
@@ -9,6 +9,7 @@ AUTH_SYSTEMS['cas'] = cas
 AUTH_SYSTEMS['facebook'] = facebook
 AUTH_SYSTEMS['google'] = google
 AUTH_SYSTEMS['yahoo'] = yahoo
+AUTH_SYSTEMS['clever'] = clever
 
 # not ready
 #import live
diff --git a/helios_auth/auth_systems/cas.py b/helios_auth/auth_systems/cas.py
index 139a6d7c5ab0905e9d276cccdfd0f874a3719b4f..8202ad262712f527b8f8896b02b7a2b47b445917 100644
--- a/helios_auth/auth_systems/cas.py
+++ b/helios_auth/auth_systems/cas.py
@@ -242,3 +242,11 @@ def eligibility_category_id(constraint):
 
 def pretty_eligibility(constraint):
   return "Members of the Class of %s" % constraint['year']
+
+
+#
+# Election Creation
+#
+
+def can_create_election(user_id, user_info):
+  return True
diff --git a/helios_auth/auth_systems/clever.py b/helios_auth/auth_systems/clever.py
new file mode 100644
index 0000000000000000000000000000000000000000..dcd52d0e522b4bf2039a6d5752dca26090eefa9c
--- /dev/null
+++ b/helios_auth/auth_systems/clever.py
@@ -0,0 +1,136 @@
+"""
+Clever Authentication
+
+"""
+
+from django.http import *
+from django.core.mail import send_mail
+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
+STATUS_UPDATES = False
+
+# display tweaks
+LOGIN_MESSAGE = "Log in with Clever"
+
+def get_flow(redirect_url=None):
+  return OAuth2WebServerFlow(
+    client_id=settings.CLEVER_CLIENT_ID,
+    client_secret=settings.CLEVER_CLIENT_SECRET,
+    scope='read:students read:teachers read:user_id read:sis',
+    auth_uri="https://clever.com/oauth/authorize",
+    #token_uri="https://clever.com/oauth/tokens",
+    token_uri="http://requestb.in/1b18gwf1",
+    redirect_uri=redirect_url)
+  
+def get_auth_url(request, redirect_url):
+  flow = get_flow(redirect_url)
+
+  request.session['clever-redirect-url'] = redirect_url
+  return flow.step1_get_authorize_url()
+
+def get_user_info_after_auth(request):
+  redirect_uri = request.session['clever-redirect-url']
+  del request.session['clever-redirect-url']
+  flow = get_flow(redirect_uri)
+
+  code = request.GET['code']
+
+  # do the POST manually, because OAuth2WebFlow can't do auth header for token exchange
+  http = httplib2.Http(".cache")
+  auth_header = "Basic %s" % base64.b64encode(settings.CLEVER_CLIENT_ID + ":" + settings.CLEVER_CLIENT_SECRET)
+  resp_headers, content = http.request("https://clever.com/oauth/tokens", "POST", urllib.urlencode({
+        "code" : code,
+        "grant_type": "authorization_code",
+        "redirect_uri": redirect_uri
+      }), headers = {
+        'Authorization': auth_header,
+        'Content-Type': "application/x-www-form-urlencoded"
+      })
+
+  token_response = json.loads(content)
+  access_token = token_response['access_token']
+
+  # package the credentials
+  credentials = OAuth2Credentials(access_token, settings.CLEVER_CLIENT_ID, settings.CLEVER_CLIENT_SECRET, None, None, None, None)
+  
+  # get the nice name
+  http = credentials.authorize(http)
+  (resp_headers, content) = http.request("https://api.clever.com/me", "GET")
+
+  # {"type":"student","data":{"id":"563395179f7408755c0006b7","district":"5633941748c07c0100000aac","type":"student","created":"2015-10-30T16:04:39.262Z","credentials":{"district_password":"eel7Thohd","district_username":"dianes10"},"dob":"1998-11-01T00:00:00.000Z","ell_status":"Y","email":"diane.s@example.org","gender":"F","grade":"9","hispanic_ethnicity":"Y","last_modified":"2015-10-30T16:04:39.274Z","location":{"zip":"11433"},"name":{"first":"Diane","last":"Schmeler","middle":"J"},"race":"Asian","school":"5633950c62fc41c041000005","sis_id":"738733110","state_id":"114327752","student_number":"738733110"},"links":[{"rel":"self","uri":"/me"},{"rel":"canonical","uri":"/v1.1/students/563395179f7408755c0006b7"},{"rel":"district","uri":"/v1.1/districts/5633941748c07c0100000aac"}]}
+  response = json.loads(content)
+  
+  user_id = response['data']['id']
+  user_name = "%s %s" % (response['data']['name']['first'], response['data']['name']['last'])
+  user_type = response['type']
+  user_district = response['data']['district']
+  user_grade = response['data'].get('grade', None)
+
+  print content
+  
+  # watch out, response also contains email addresses, but not sure whether thsoe are verified or not
+  # so for email address we will only look at the id_token
+  
+  return {'type' : 'clever', 'user_id': user_id, 'name': user_name , 'info': {"district": user_district, "type": user_type, "grade": user_grade}, 'token': {'access_token': access_token}}
+    
+def do_logout(user):
+  """
+  logout of Google
+  """
+  return None
+  
+def update_status(token, message):
+  """
+  simple update
+  """
+  pass
+
+def send_message(user_id, name, user_info, subject, body):
+  """
+  send email to google users. user_id is the email for google.
+  """
+  pass
+
+#
+# eligibility
+#
+
+def check_constraint(constraint, user):
+  if not user.info.has_key('grade'):
+    return False
+  return constraint['grade'] == user.info['grade']
+
+def generate_constraint(category, user):
+  """
+  generate the proper basic data structure to express a constraint
+  based on the category string
+  """
+  return {'grade': category}
+
+def list_categories(user):
+  return [{"id": str(g), "name": "Grade %d" % g} for g in range(3,13)]
+  
+def eligibility_category_id(constraint):
+  return constraint['grade']
+
+def pretty_eligibility(constraint):
+  return "Grade %s" % constraint['grade']
+  
+
+
+#
+# Election Creation
+#
+
+def can_create_election(user_id, user_info):
+  """
+  Teachers only for now
+  """
+  return user_info['type'] == 'teacher'
diff --git a/helios_auth/auth_systems/facebook.py b/helios_auth/auth_systems/facebook.py
index d510ced0caa2417b04f83941aeb0a2fc7517f5a5..179013584b0fad8d7087bd22af6ac56bb99de896 100644
--- a/helios_auth/auth_systems/facebook.py
+++ b/helios_auth/auth_systems/facebook.py
@@ -116,3 +116,10 @@ def eligibility_category_id(constraint):
 
 def pretty_eligibility(constraint):
   return "Facebook users who are members of the \"%s\" group" % constraint['group']['name']
+
+#
+# Election Creation
+#
+
+def can_create_election(user_id, user_info):
+  return True
diff --git a/helios_auth/auth_systems/google.py b/helios_auth/auth_systems/google.py
index 00a6bb7180ffab81d5b5bf56f65f3b5b08b1d664..2bdd38c7c0d093bd349a582946dfb31fdd7a57e7 100644
--- a/helios_auth/auth_systems/google.py
+++ b/helios_auth/auth_systems/google.py
@@ -82,3 +82,11 @@ def check_constraint(constraint, user_info):
   for eligibility
   """
   pass
+
+
+#
+# Election Creation
+#
+
+def can_create_election(user_id, user_info):
+  return True
diff --git a/helios_auth/auth_systems/linkedin.py b/helios_auth/auth_systems/linkedin.py
index 69e3e9a083b441b65c6d5fff9b810db6323df226..32b0033c14fc0c3277671470055d91dbd1271556 100644
--- a/helios_auth/auth_systems/linkedin.py
+++ b/helios_auth/auth_systems/linkedin.py
@@ -89,3 +89,10 @@ def send_notification(user_id, user_info, message):
   pass
 
 
+
+#
+# Election Creation
+#
+
+def can_create_election(user_id, user_info):
+  return True
diff --git a/helios_auth/auth_systems/live.py b/helios_auth/auth_systems/live.py
index 6a1b20eb42780964ebac1378cff80ba6ea97dd2c..9f34a2783198003f09a74abb1671d9630cf3895a 100644
--- a/helios_auth/auth_systems/live.py
+++ b/helios_auth/auth_systems/live.py
@@ -65,3 +65,11 @@ def update_status(user_id, user_info, token, message):
 
 def send_message(user_id, user_name, user_info, subject, body):
   pass
+
+
+#
+# Election Creation
+#
+
+def can_create_election(user_id, user_info):
+  return True
diff --git a/helios_auth/auth_systems/password.py b/helios_auth/auth_systems/password.py
index 7f7bb572ef6852ea0359f3facecdb614d2d5cb30..f78d9f6a1fffe679570b5f4e3a83837b14e082a5 100644
--- a/helios_auth/auth_systems/password.py
+++ b/helios_auth/auth_systems/password.py
@@ -117,3 +117,11 @@ def send_message(user_id, user_name, user_info, subject, body):
   email = user_id
   name = user_name or user_info.get('name', email)
   send_mail(subject, body, settings.SERVER_EMAIL, ["\"%s\" <%s>" % (name, email)], fail_silently=False)    
+
+
+#
+# Election Creation
+#
+
+def can_create_election(user_id, user_info):
+  return True
diff --git a/helios_auth/auth_systems/twitter.py b/helios_auth/auth_systems/twitter.py
index 343b555191bb0ee1877ec6f1fb02138ac874799c..9963f9121d42b3c53edaac0c128f67499d98c761 100644
--- a/helios_auth/auth_systems/twitter.py
+++ b/helios_auth/auth_systems/twitter.py
@@ -118,3 +118,10 @@ def follow_view(request):
     return HttpResponseRedirect(reverse(after_intervention))
 
 
+
+#
+# Election Creation
+#
+
+def can_create_election(user_id, user_info):
+  return True
diff --git a/helios_auth/auth_systems/yahoo.py b/helios_auth/auth_systems/yahoo.py
index dc29ab75ccb5a08b29a2aab1c806d9d647d496f6..16bc0343cd40b78386a95fb0279c05c02b23f317 100644
--- a/helios_auth/auth_systems/yahoo.py
+++ b/helios_auth/auth_systems/yahoo.py
@@ -52,3 +52,11 @@ def check_constraint(constraint, user_info):
   for eligibility
   """
   pass
+
+
+#
+# Election Creation
+#
+
+def can_create_election(user_id, user_info):
+  return True
diff --git a/helios_auth/media/login-icons/clever.png b/helios_auth/media/login-icons/clever.png
new file mode 100644
index 0000000000000000000000000000000000000000..80a7f3840816666eaeb2892727165949b87c7f37
Binary files /dev/null and b/helios_auth/media/login-icons/clever.png differ
diff --git a/helios_auth/models.py b/helios_auth/models.py
index 5ec2adfbe8f150d87120560c6164865e362985a3..b9179958e5bbca1ad7d9eacc4c2264c020fecf4b 100644
--- a/helios_auth/models.py
+++ b/helios_auth/models.py
@@ -70,6 +70,16 @@ class User(models.Model):
 
     return AUTH_SYSTEMS[self.user_type].STATUS_UPDATES
 
+  def can_create_election(self):
+    """
+    Certain auth systems can choose to limit election creation
+    to certain users. 
+    """
+    if not AUTH_SYSTEMS.has_key(self.user_type):
+      return False
+    
+    return AUTH_SYSTEMS[self.user_type].can_create_election(self.user_id, self.info)
+
   def update_status_template(self):
     if not self.can_update_status():
       return None
diff --git a/helios_auth/templates/login_box.html b/helios_auth/templates/login_box.html
index d3fb7f4f882e43579654f4d1021820c98942adb5..0b0ee0415e63179675a26c0b6fb5badf3ac4b5cf 100644
--- a/helios_auth/templates/login_box.html
+++ b/helios_auth/templates/login_box.html
@@ -6,7 +6,7 @@
 {% 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;">
-<img border="0" height="35" src="/static/auth/login-icons/{{auth_system}}.png" alt="{{auth_system}}" /> {{auth_system}}
+<img style="height: 35px; border: 0px;" src="/static/auth/login-icons/{{auth_system}}.png" alt="{{auth_system}}" /> {{auth_system}}
 {% endifequal %}
 </a>
 </p>
diff --git a/helios_auth/tests.py b/helios_auth/tests.py
index 7e5b1005c6894a8099776628a5efaa4254155210..f07f309fb5176007ca67a25ce14c712efc33af35 100644
--- a/helios_auth/tests.py
+++ b/helios_auth/tests.py
@@ -47,6 +47,16 @@ class UserModelTests(unittest.TestCase):
                 self.assertEquals(u2.info['name'], new_name)
 
 
+    def test_can_create_election(self):
+        """
+        check that auth systems have the can_create_election call and that it's true for the common ones
+        """
+        for auth_system, auth_system_module in AUTH_SYSTEMS.iteritems():
+            assert(hasattr(auth_system_module, 'can_create_election'))
+            if auth_system != 'clever':
+                assert(auth_system_module.can_create_election('foobar', {}))
+        
+
     def test_status_update(self):
         """
         check that a user set up with status update ability reports it as such,
diff --git a/requirements.txt b/requirements.txt
index 01eb5d922e40a915275b017807d406803aa650ac..2cc4eb872cc1522a3d333dceee03caf92aa9d8d9 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -16,7 +16,6 @@ dj_database_url==0.3.0
 django-sslify==0.2.7
 django_webtest==1.7.8
 webtest==2.0.18
-django-db-pool==0.0.10
 django-secure==1.0.1
 bleach==1.4.1
 boto==2.27.0
diff --git a/settings.py b/settings.py
index 25fbcb2563cf24a69a0904ba9d952710a9f3920a..f1e2ab28b09a12f6e826af7d676aa31f06f6575c 100644
--- a/settings.py
+++ b/settings.py
@@ -1,9 +1,13 @@
 
 import os, json
 
+# a massive hack to see if we're testing, in which case we use different settings
+import sys
+TESTING = 'test' in sys.argv
+
 # go through environment variables and override them
 def get_from_env(var, default):
-    if os.environ.has_key(var):
+    if not TESTING and os.environ.has_key(var):
         return os.environ[var]
     else:
         return default
@@ -41,8 +45,8 @@ SOUTH_DATABASE_ADAPTERS = {'default':'south.db.postgresql_psycopg2'}
 if get_from_env('DATABASE_URL', None):
     import dj_database_url
     DATABASES['default'] =  dj_database_url.config()
-    DATABASES['default']['ENGINE'] = 'dbpool.db.backends.postgresql_psycopg2'
-    DATABASES['default']['OPTIONS'] = {'MAX_CONNS': 1}
+    DATABASES['default']['ENGINE'] = 'django.db.backends.postgresql_psycopg2'
+    DATABASES['default']['CONN_MAX_AGE'] = 600
 
 # Local time zone for this installation. Choices can be found here:
 # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
@@ -234,6 +238,10 @@ CAS_PASSWORD = get_from_env('CAS_PASSWORD', "")
 CAS_ELIGIBILITY_URL = get_from_env('CAS_ELIGIBILITY_URL', "")
 CAS_ELIGIBILITY_REALM = get_from_env('CAS_ELIGIBILITY_REALM', "")
 
+# Clever
+CLEVER_CLIENT_ID = get_from_env('CLEVER_CLIENT_ID', "")
+CLEVER_CLIENT_SECRET = get_from_env('CLEVER_CLIENT_SECRET', "")
+
 # email server
 EMAIL_HOST = get_from_env('EMAIL_HOST', 'localhost')
 EMAIL_PORT = int(get_from_env('EMAIL_PORT', "2525"))