# HG changeset patch # User Peter Sanchez # Date 1289339125 28800 # Tue Nov 09 13:45:25 2010 -0800 # Node ID 1eb9dc60217eedc4201fcba21397834e1e667510 # Parent db6ab67104203985f8161bbae05c2436f838cd2a Moved status syncing to class based. Mainly for easily testing responses diff --git a/twittersync/helpers.py b/twittersync/helpers.py --- a/twittersync/helpers.py +++ b/twittersync/helpers.py @@ -15,35 +15,42 @@ from django.utils import simplejson as json -def build_url(qdict): - return 'http://api.twitter.com/1/statuses/user_timeline.json?%s' % \ +class TwitterSyncHelper(object): + def __init__(self, account): + self.account = account + + def build_url(self, qdict): + return 'http://api.twitter.com/1/statuses/user_timeline.json?%s' % \ qdict.urlencode() + def send_request(self, latest=None): + qdict = QueryDict('', mutable=True) + opener = urllib2.build_opener() + opener.addheaders = [('User-agent', 'django-twittersync/0.1')] + qdict['screen_name'] = self.account.screen_name + qdict['trim_user'] = 'true' + + if latest is not None: + qdict['since_id'] = latest.status_id -def save_status_update(account, result): - return TwitterStatus.objects.get_or_create( - status_id=result['id_str'], - author=account, - content=result['text'], - created_date=parse(result['created_at']), - ) - + return opener.open(self.build_url(qdict)) -def sync_twitter_account(account): - qdict = QueryDict('', mutable=True) - opener = urllib2.build_opener() - opener.addheaders = [('User-agent', 'django-twittersync/0.1')] - qdict['screen_name'] = account.screen_name - qdict['trim_user'] = 'true' - - # If previous updates exist, only get newer tweets - try: - latest = account.tweets.latest() - qdict['since_id'] = latest.status_id - except TwitterStatus.DoesNotExist: - pass + def save_status_update(self, result): + return TwitterStatus.objects.get_or_create( + status_id=result['id_str'], + author=self.account, + content=result['text'], + created_date=parse(result['created_at']), + ) - res = opener.open(build_url(qdict)) - results = json.load(res) - for result in results: - save_status_update(account, result) + def sync_twitter_account(self): + # If previous updates exist, only get newer tweets + try: + latest = self.account.tweets.latest() + except TwitterStatus.DoesNotExist: + latest = None + + res = self.send_request(latest) + results = json.load(res) + for result in results: + self.save_status_update(result) diff --git a/twittersync/management/commands/sync_twitter_accounts.py b/twittersync/management/commands/sync_twitter_accounts.py --- a/twittersync/management/commands/sync_twitter_accounts.py +++ b/twittersync/management/commands/sync_twitter_accounts.py @@ -4,7 +4,7 @@ from django.http import QueryDict from django.core.management.base import NoArgsCommand from twittersync.models import TwitterAccount -from twittersync.helpers import sync_twitter_account +from twittersync.helpers import TwitterSyncHelper class Command(NoArgsCommand): @@ -12,4 +12,4 @@ def handle_noargs(self, **options): for account in TwitterAccount.active.all(): - sync_twitter_account(account) + TwitterSyncHelper(account).sync_twitter_account() diff --git a/twittersync/tests.py b/twittersync/tests.py new file mode 100644 --- /dev/null +++ b/twittersync/tests.py @@ -0,0 +1,224 @@ +from django.conf import settings +from django.utils import unittest +from django.test.client import Client + +from helpers import TwitterSyncHelper +from models import TwitterAccount, TwitterStatus + + +class MockResponse(object): + def __init__(self, status, data=None): + self.status = status + self.code = status + self.data = data + + def info(self): + return self + + def read(self): + if self.data is None: + return '' + data, self.data = self.data, None + return data + + +class TestBase(unittest.TestCase): + def setUp(self): + self._old_send_request = TwitterSyncHelper.send_request + TwitterSyncHelper.send_request = self._send_request + self.client = Client() + self.responses = [] + self.requests = [] + + def tearDown(self): + TwitterSyncHelper.send_request = self._old_send_request + + def _send_request(self, latest=None): + self.requests.append((latest,)) + return self.responses.pop() + + +class TwitterSyncTest(TestBase): + def test_new_tweets(self): + # Taken from http://dev.twitter.com/console + tweet_json = '''[ + { + "coordinates": null, + "favorited": false, + "created_at": "Tue Nov 09 20:42:19 +0000 2010", + "truncated": false, + "id_str": "2098682128244736", + "in_reply_to_user_id_str": null, + "text": "Loonnnggggg day!", + "contributors": null, + "id": 2098682128244736, + "in_reply_to_status_id_str": null, + "retweet_count": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "user": { + "profile_background_tile": false, + "name": "Peter Sanchez", + "profile_sidebar_border_color": "C6E2EE", + "profile_sidebar_fill_color": "DAECF4", + "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/67635284\/img1_normal.jpg", + "location": "Los Angeles, CA", + "created_at": "Thu Apr 17 17:51:56 +0000 2008", + "follow_request_sent": false, + "id_str": "14423585", + "profile_link_color": "1F98C7", + "favourites_count": 0, + "url": "http:\/\/www.petersanchez.com", + "contributors_enabled": false, + "utc_offset": -28800, + "id": 14423585, + "listed_count": 10, + "profile_use_background_image": true, + "profile_text_color": "663B12", + "protected": false, + "followers_count": 4771, + "lang": "en", + "notifications": false, + "profile_background_color": "C6E2EE", + "time_zone": "Pacific Time (US & Canada)", + "verified": false, + "geo_enabled": false, + "description": "Entrepreneur, Internet Marketer, Software Engineer, Copywriter and Internet Junkie. How do ya like that?", + "friends_count": 5197, + "statuses_count": 3, + "profile_background_image_url": "http:\/\/a1.twimg.com\/profile_background_images\/3271040\/bgimage.jpg", + "show_all_inline_media": false, + "following": true, + "screen_name": "petersanchez" + }, + "source": "web", + "place": null, + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Tue Nov 09 08:04:40 +0000 2010", + "truncated": false, + "id_str": "1908014869118976", + "in_reply_to_user_id_str": null, + "text": "New Blog Post: NorthWoods Inn - Hallows Eve 2010: On Hallows Eve 2010 I went to dinner at the NorthWoods Inn res... http:\/\/bit.ly\/9VFsEo", + "contributors": null, + "id": 1908014869118976, + "in_reply_to_status_id_str": null, + "retweet_count": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "user": { + "profile_background_tile": false, + "name": "Peter Sanchez", + "profile_sidebar_border_color": "C6E2EE", + "profile_sidebar_fill_color": "DAECF4", + "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/67635284\/img1_normal.jpg", + "location": "Los Angeles, CA", + "created_at": "Thu Apr 17 17:51:56 +0000 2008", + "follow_request_sent": false, + "id_str": "14423585", + "profile_link_color": "1F98C7", + "favourites_count": 0, + "contributors_enabled": false, + "url": "http:\/\/www.petersanchez.com", + "utc_offset": -28800, + "id": 14423585, + "profile_use_background_image": true, + "listed_count": 10, + "profile_text_color": "663B12", + "protected": false, + "followers_count": 4772, + "lang": "en", + "notifications": false, + "verified": false, + "profile_background_color": "C6E2EE", + "geo_enabled": false, + "time_zone": "Pacific Time (US & Canada)", + "description": "Entrepreneur, Internet Marketer, Software Engineer, Copywriter and Internet Junkie. How do ya like that?", + "friends_count": 5197, + "profile_background_image_url": "http:\/\/a1.twimg.com\/profile_background_images\/3271040\/bgimage.jpg", + "statuses_count": 2, + "following": true, + "show_all_inline_media": false, + "screen_name": "petersanchez" + }, + "source": "web", + "place": null, + "in_reply_to_status_id": null + }, + { + "coordinates": null, + "favorited": false, + "created_at": "Tue Nov 09 00:25:36 +0000 2010", + "truncated": false, + "id_str": "1792487194632192", + "in_reply_to_user_id_str": null, + "text": "I'm in the pursuit of happiness and I know....", + "contributors": null, + "id": 1792487194632192, + "in_reply_to_status_id_str": null, + "retweet_count": null, + "geo": null, + "retweeted": false, + "in_reply_to_user_id": null, + "in_reply_to_screen_name": null, + "user": { + "profile_background_tile": false, + "name": "Peter Sanchez", + "profile_sidebar_border_color": "C6E2EE", + "profile_sidebar_fill_color": "DAECF4", + "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/67635284\/img1_normal.jpg", + "location": "Los Angeles, CA", + "created_at": "Thu Apr 17 17:51:56 +0000 2008", + "follow_request_sent": false, + "id_str": "14423585", + "profile_link_color": "1F98C7", + "favourites_count": 0, + "contributors_enabled": false, + "url": "http:\/\/www.petersanchez.com", + "utc_offset": -28800, + "id": 14423585, + "profile_use_background_image": true, + "listed_count": 10, + "profile_text_color": "663B12", + "protected": false, + "followers_count": 4771, + "lang": "en", + "notifications": false, + "profile_background_color": "C6E2EE", + "verified": false, + "geo_enabled": false, + "time_zone": "Pacific Time (US & Canada)", + "description": "Entrepreneur, Internet Marketer, Software Engineer, Copywriter and Internet Junkie. How do ya like that?", + "profile_background_image_url": "http:\/\/a1.twimg.com\/profile_background_images\/3271040\/bgimage.jpg", + "friends_count": 5197, + "statuses_count": 3, + "following": false, + "show_all_inline_media": false, + "screen_name": "petersanchez" + }, + "source": "web", + "place": null, + "in_reply_to_status_id": null + } +] +''' + # Clear out all tweets first + TwitterAccount.objects.all().delete() + TwitterStatus.objects.all().delete() + + account = TwitterAccount.objects.create( + screen_name='testuser', + is_active=True, + ) + self.assertEquals(TwitterAccount.objects.count(), 1) + + self.responses.append(MockResponse(200, data=tweet_json)) + TwitterSyncHelper(account).sync_twitter_account() + self.assertEquals(TwitterStatus.objects.count(), 3)