Skip to content

Commit

Permalink
Call tool can now load a remote Google spreadsheet which overrides de…
Browse files Browse the repository at this point in the history
…fault campaign behaviors on a per-state basis. Using this, you can change the order of the house rep senator calls, always call an individual politician first, or add a special name and number to call before any of the politicians. Also we are now able to randomize the campaign phone call order.
  • Loading branch information
rubbingalcoholic committed Sep 2, 2014
1 parent 706441b commit 8719ece
Show file tree
Hide file tree
Showing 8 changed files with 1,062 additions and 856 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ venv
*.pyc
*.db
.env
src
50 changes: 36 additions & 14 deletions app.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import random
import urlparse
import json

from datetime import datetime, timedelta

Expand All @@ -18,6 +19,8 @@

from models import db, aggregate_stats, log_call, call_count
from political_data import PoliticalData
from cache_handler import CacheHandler


app = Flask(__name__)

Expand All @@ -28,9 +31,13 @@

db.init_app(app)

# Optional Redis cache, for caching Google spreadsheet campaign overrides
cache_handler = CacheHandler(app.config['REDIS_URL'])


call_methods = ['GET', 'POST']

data = PoliticalData()
data = PoliticalData(cache_handler)


def make_cache_key(*args, **kwargs):
Expand Down Expand Up @@ -82,6 +89,10 @@ def parse_params(r):
params['repIds'] = data.locate_member_ids(
params['zipcode'], campaign)

# delete the zipcode, since the repIds are in a particular order and
# will be passed around from endpoint to endpoint hereafter anyway.
del params['zipcode']

if 'random_choice' in campaign:
# pick a random choice among a selected set of members
params['repIds'] = [random.choice(campaign['random_choice'])]
Expand Down Expand Up @@ -254,28 +265,39 @@ def make_single_call():
if not params or not campaign:
abort(404)

resp = twilio.twiml.Response()

i = int(request.values.get('call_index', 0))
params['call_index'] = i
member = [l for l in data.legislators
if l['bioguide_id'] == params['repIds'][i]][0]
congress_phone = member['phone']
full_name = unicode("{} {}".format(
member['firstname'], member['lastname']), 'utf8')

resp = twilio.twiml.Response()
if "SPECIAL_CALL_" in params['repIds'][i]:

special = json.loads(params['repIds'][i].replace("SPECIAL_CALL_", ""))
to_phone = special['number']
full_name = special['name']
play_or_say(resp, campaign.get('msg_special_call_intro',
campaign['msg_rep_intro']), name=full_name)

if 'voted_with_list' in campaign and \
params['repIds'][i] in campaign['voted_with_list']:
play_or_say(
resp, campaign['msg_repo_intro_voted_with'], name=full_name)
else:
play_or_say(resp, campaign['msg_rep_intro'], name=full_name)

member = [l for l in data.legislators
if l['bioguide_id'] == params['repIds'][i]][0]
to_phone = member['phone']
full_name = unicode("{} {}".format(
member['firstname'], member['lastname']), 'utf8')

if 'voted_with_list' in campaign and \
params['repIds'][i] in campaign['voted_with_list']:
play_or_say(
resp, campaign['msg_repo_intro_voted_with'], name=full_name)
else:
play_or_say(resp, campaign['msg_rep_intro'], name=full_name)

if app.debug:
print u'DEBUG: Call #{}, {} ({}) from {} in make_single_call()'.format(
i, full_name, congress_phone, params['userPhone'])
i, full_name, to_phone, params['userPhone'])

resp.dial(congress_phone, callerId=params['userPhone'],
resp.dial(to_phone, callerId=params['userPhone'],
timeLimit=app.config['TW_TIME_LIMIT'],
timeout=app.config['TW_TIMEOUT'], hangupOnStar=True,
action=url_for('call_complete', **params))
Expand Down
27 changes: 27 additions & 0 deletions cache_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from redis import Redis

class CacheHandler():

redis_conn = None

def __init__(self, redis_url):

if redis_url:
self.redis_conn = Redis.from_url(redis_url)

def get(self, key, default):

if self.redis_conn == None:
return default

return self.redis_conn.get(key) or default

def set(self, key, val, expire=None):

if self.redis_conn == None:
return

if expire == None:
self.redis_conn.set(key, val)
else:
self.redis_conn.setex(key, val, expire)
6 changes: 4 additions & 2 deletions config.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ class Config(object):
TW_CLIENT = twilio.rest.TwilioRestClient(
os.environ.get('TWILIO_DEV_ACCOUNT_SID'),
os.environ.get('TWILIO_DEV_AUTH_TOKEN'))
TW_NUMBER = '5005550006' # development number
# TW_NUMBER = '5005550006' # development number # JL NOTE ~ unnecessary

TASKFORCE_KEY = os.environ.get('TASKFORCE_KEY')
SUNLIGHTLABS_KEY = os.environ.get('SUNLIGHTLABS_KEY')

REDIS_URL = os.environ.get('REDISTOGO_URL') or None # JL NOTE ~ optional

# limit on the length of the call
TW_TIME_LIMIT = 60 * 20 # 4 minutes

Expand All @@ -42,7 +44,7 @@ class ConfigProduction(Config):
TW_CLIENT = twilio.rest.TwilioRestClient(
os.environ.get('TWILIO_ACCOUNT_SID'),
os.environ.get('TWILIO_AUTH_TOKEN'))
TW_NUMBER = os.environ.get('TWILIO_NUMBER')
# TW_NUMBER = os.environ.get('TWILIO_NUMBER') # JL NOTE ~ unnecessary

SECRET_KEY = os.environ.get('SECRET_KEY')

Expand Down
Loading

0 comments on commit 8719ece

Please sign in to comment.