Skip to content
This repository has been archived by the owner on Mar 16, 2020. It is now read-only.

Commit

Permalink
adding a lookup counter
Browse files Browse the repository at this point in the history
  • Loading branch information
peterbe committed Oct 11, 2011
1 parent d5a3462 commit 3760cf7
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 64 deletions.
118 changes: 56 additions & 62 deletions handlers.py
Expand Up @@ -64,6 +64,21 @@ def get(self):
@route('/jsonp', name='jsonp')
class FollowsHandler(BaseHandler, tornado.auth.TwitterMixin):

def increment_lookup_count(self, username, usernames, jsonp=False):
if jsonp:
key = 'lookups:jsonp'
else:
key = 'lookups:json'
if not isinstance(usernames, int):
usernames = len(usernames)
self.redis.incr(key)

key = 'lookups:username:%s' % username
self.redis.incr(key)

key = 'lookups:usernames'
self.redis.incr(key, usernames)

@tornado.web.asynchronous
@tornado.gen.engine
def get(self):
Expand Down Expand Up @@ -123,15 +138,15 @@ def get(self):
self.finish()
return

self.increment_lookup_count(this_username, len(usernames), jsonp=jsonp)

results = {}
# pick some up already from the cache
_drop = set()
for username in usernames:
key = 'follows:%s:%s' % (this_username, username)
value = self.redis.get(key)
if value is not None:
#print repr(username)
#print "\t", repr(value)
results[username] = bool(int(value))
_drop.add(username)
usernames -= _drop
Expand Down Expand Up @@ -213,6 +228,12 @@ def get_next_url(self):
@route('/auth/twitter/', name='auth_twitter')
class TwitterAuthHandler(BaseAuthHandler, tornado.auth.TwitterMixin):

def increment_authentication_count(self, username):
key = 'auths:username:%s' % username
self.redis.incr(key)
key = 'auths:total'
self.redis.incr(key)

@tornado.web.asynchronous
def get(self):
if self.get_argument("oauth_token", None):
Expand All @@ -236,6 +257,8 @@ def _on_auth(self, user_struct):
user['access_token'] = access_token
user.save()

self.increment_authentication_count(username)

self.set_secure_cookie("user",
str(user['_id']),
expires_days=30, path='/')
Expand Down Expand Up @@ -401,72 +424,14 @@ def get(self):
@route('/everyone', name='everyone')
class EveryoneIFollowHandler(BaseHandler, tornado.auth.TwitterMixin):

@tornado.web.asynchronous
@tornado.gen.engine
#@tornado.web.asynchronous
#@tornado.gen.engine
def get(self):

current_user = self.get_current_user()
if not current_user:
self.redirect(self.reverse_url('auth_twitter'))
return

# this_username = current_user['username']
# access_token = current_user['access_token']
# key = 'friends:%s' % this_username
# result = self.redis.get(key)
# if result is None:
# result = yield tornado.gen.Task(self.twitter_request,
# "/friends/ids",
# screen_name=this_username,
# access_token=access_token
# )
# self.redis.setex(key, json_encode(result), 60 * 60)
# else:
# result = json_decode(result)
# # now turn these IDs into real screen names
# unknown = []
# screen_names = []
# for id_ in result:
# user = self.db.User.find_one({'user_id': id_})
# if user:
# screen_names.append(user['username'])
# else:
# key = 'screen_name:%s' % id_
# screen_name = self.redis.get(key)
# if screen_name is None:
# unknown.append(id_)
# else:
# screen_names.append(screen_name)
#
# buckets = utils.bucketize(unknown, 100)
#
# for bucket in buckets:
# users = None
# attempts = 0
# while True:
# users = yield tornado.gen.Task(self.twitter_request,
# "/users/lookup",
# user_id=','.join(str(x) for x in bucket)
# )
# if users is not None:
# break
# else:
# from time import sleep
# sleep(1)
# attempts += 1
# if attempts > 3:
# raise HTTPError(500, "Unable to connect to twitter")
# for user in users:
# username = user['screen_name']
# key = 'screen_name:%s' % user['id']
# self.redis.setex(key, username, 7 * 24 * 60 * 60)
# screen_names.append(username)
#
# assert len(result) == len(screen_names)
#
# screen_names.sort()
options = {}
# options['screen_names'] = screen_names
options['page_title'] = "Everyone I follow"
self.render('everyone.html', **options)

Expand Down Expand Up @@ -538,3 +503,32 @@ def get(self):
screen_names.sort()
self.write_json(screen_names)
self.finish()


@route('/lookups', name='lookups')
class LookupsHandler(BaseHandler):

def get_lookups(self, username=None):
data = {}
data['lookups_json'] = self.redis.get('lookups:json') or 0
data['lookups_jsonp'] = self.redis.get('lookups:jsonp') or 0
data['auths'] = self.redis.get('auths:total') or 0
data['lookups_usernames'] = self.redis.get('lookups:usernames') or 0
if username:
print "NotImplmented"
for key, value in data.items():
data[key] = int(value)
return data

def get(self):
options = {}
options['page_title'] = "Lookups"
options.update(self.get_lookups())
self.render('lookups.html', **options)

@route('/lookups.json', name='lookups_json')
class LookupsJSONHandler(LookupsHandler):

def get(self):
data = self.get_lookups()
self.write_json(data)
3 changes: 3 additions & 0 deletions static/css/style.css
Expand Up @@ -116,3 +116,6 @@ img.screengrab1 {float:left; margin-top: 20px; opacity:0.7;}
img.screengrab {float: left; margin-right: 10px;}
h3.confused {font-size:35px;}
span.grey {color:#c1c1c1;}

header h1 { }
header p#home-link { font-size:0.8em; margin-top:0; }
50 changes: 50 additions & 0 deletions static/js/lookups.js
@@ -0,0 +1,50 @@

function compareAssociativeArrays(a, b) {
function nrKeys(a) {
var i = 0;
for (key in a) {
i++;
}
return i;
}
if (a == b) {
return true;
}
if (nrKeys(a) != nrKeys(b)) {
return false;
}
for (key in a) {
if (a[key] != b[key]) {
return false;
}
}
return true;
}


var previous = {}, incr = 0; // global
function update() {
$.getJSON(JSON_URL, function(response) {
$('#lookups-total').text(response.lookups_json + response.lookups_jsonp);
$('#lookups-json').text(response.lookups_json);
$('#lookups-jsonp').text(response.lookups_jsonp);
$('#lookups-usernames').text(response.lookups_usernames);
$('#auths').text(response.auths);
var change = !compareAssociativeArrays(response, previous);
previous = response;

var t;
if (change) {
t = 1;
incr = 0;
} else {
t = Math.min(3 + incr, 10);
incr += 0.1;
}
console.log(Math.ceil(t*1000));
setTimeout(update, Math.ceil(t * 1000));
});
}
$(function() {
setTimeout(update, 5 * 1000);
});
1 change: 1 addition & 0 deletions templates/base.html
Expand Up @@ -13,6 +13,7 @@
<header>

<h1><a href="/">Too Cool For Me<span class="q">?</span></a></h1>
{% block home_link %}<p id="home-link"><a href="/">&larr; home</a></p>{% end %}
{% block extra_header %}
{% end %}
</header>
Expand Down
1 change: 1 addition & 0 deletions templates/home.html
@@ -1,5 +1,6 @@
{% extends "base.html" %}

{% block home_link %}{% end %}
{% block extra_header %}
<h3>Find out who on <a href="https://twitter.com/">Twitter</a> follows you back <span class="grey">(and who is just too cool for you)</span></h3>
{% end %}
Expand Down
47 changes: 47 additions & 0 deletions templates/lookups.html
@@ -0,0 +1,47 @@
{% extends "base.html" %}

{% block extra_head %}
<style>
th, td { background-color:#efefef; padding:12px 27px; }
th { text-align:left; }
td { font-size:2.5em; font-weight:bold; }
</style>
{% end %}


{% block content %}
<h1>Lookups</h1>

<p>In other words, is this app being actively used?</p>

<table>
<tr>
<th>Total number of usernames looked up:</th>
<td id="lookups-usernames">{{ lookups_usernames }}</td>
</tr>
<tr>
<th>Twitter requests total:</th>
<td id="lookups-total">{{ lookups_json + lookups_jsonp }}</td>
</tr>
<tr>
<th>Twitter requests by JSON:</th>
<td id="lookups-json">{{ lookups_json }}</td>
</tr>
<tr>
<th>Twitter requests by JSONP:</th>
<td id="lookups-jsonp">{{ lookups_jsonp }}</td>
</tr>
<tr>
<th>Authentications:</th>
<td id="auths">{{ auths }}</td>
</tr>
</table>
{% end %}

{% block extra_js %}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
<script>
var JSON_URL = '{{ reverse_url('lookups_json') }}';
</script>
<script src="{{ static_url('js/lookups.js') }}"></script>
{% end %}
2 changes: 1 addition & 1 deletion templates/test.html
Expand Up @@ -6,7 +6,7 @@
<p>Type some Twitter user names below to see who's too cool for you.<br>
Also include some user names you know follow you:</p>
<div style="float:left">
<textarea name="usernames" cols="20" rows="20">ConanOBrien
<textarea name="usernames" cols="20" rows="20">katyperry
justinbieber
</textarea><input type="submit" value="Test">
</div>
Expand Down
14 changes: 13 additions & 1 deletion tests/test_handlers.py
Expand Up @@ -40,6 +40,9 @@ def test_twitter_login(self):
self.assertEqual(response.code, 200)
self.assertTrue('Login with Twitter' in response.body)

self.assertEqual(int(self.redis.get('auths:total')), 1)
self.assertEqual(int(self.redis.get('auths:username:peterbe')), 1)

def test_twitter_login_twitter_failing(self):
TwitterAuthHandler.get_authenticated_user = \
make_twitter_get_authenticated_user_callback(None)
Expand Down Expand Up @@ -98,6 +101,7 @@ def test_test_service(self):


def test_json(self):
self.assertEqual(self.redis.get('lookups:json'), None)
FollowsHandler.twitter_request = \
make_mock_twitter_request({u'relationship': {
u'target': {u'followed_by': False,
Expand Down Expand Up @@ -132,6 +136,10 @@ def test_json(self):
struct = json.loads(response.body)
self.assertEqual(struct['obama'], False)

self.assertEqual(int(self.redis.get('lookups:json')), 2)
self.assertEqual(int(self.redis.get('lookups:username:peterbe')), 2)
self.assertEqual(int(self.redis.get('lookups:usernames')), 2)

def test_json_with_overriding_you(self):
FollowsHandler.twitter_request = \
make_mock_twitter_request({u'relationship': {
Expand Down Expand Up @@ -192,6 +200,11 @@ def test_jsonp(self):
self.assertEqual(response.code, 200)
self.assertEqual(response.body, 'FOO({"obama": false})')

self.assertEqual(int(self.redis.get('lookups:json')), 0)
self.assertEqual(int(self.redis.get('lookups:jsonp')), 2)
self.assertEqual(int(self.redis.get('lookups:username:peterbe')), 2)
self.assertEqual(int(self.redis.get('lookups:usernames')), 2)

def test_jsonp(self):
url = self.reverse_url('jsonp')
response = self.client.get(url, {'username': 'obama'})
Expand All @@ -201,7 +214,6 @@ def test_jsonp(self):
self.assertEqual(response.code, 200)
self.assertTrue(response.body.startswith('FOO({"ERROR":'))


def test_following_none_cached(self):
FollowsHandler.twitter_request = \
make_mock_twitter_request([
Expand Down

0 comments on commit 3760cf7

Please sign in to comment.