From 173adee4a68b3a01292a3878e7fc33fa9622ee32 Mon Sep 17 00:00:00 2001 From: Mike Helmick Date: Tue, 25 Jun 2013 21:58:10 -0400 Subject: [PATCH 1/2] HTML for tweet fixes #224 --- twython/api.py | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/twython/api.py b/twython/api.py index d265186..c67868f 100644 --- a/twython/api.py +++ b/twython/api.py @@ -407,3 +407,41 @@ class Twython(EndpointsMixin, object): if is_py2 and isinstance(text, (str)): return Twython.unicode2utf8(text) return str(text) + + @staticmethod + def html_for_tweet(tweet, use_display_url=True, use_expanded_url=False): + if 'retweeted_status' in tweet: + tweet = tweet['retweeted_status'] + + if 'entities' in tweet: + text = tweet['text'] + entities = tweet['entities'] + + # Mentions + for entity in entities['user_mentions']: + start, end = entity['indices'][0], entity['indices'][1] + + mention_html = '@%(screen_name)s' + text = text.replace(tweet['text'][start:end], mention_html % {'url': entity['screen_name']}) + + # Hashtags + for entity in entities['hashtags']: + start, end = entity['indices'][0], entity['indices'][1] + + hashtag_html = '#%(hashtag)s' + text = text.replace(tweet['text'][start:end], hashtag_html % {'hashtag': entity['text']}) + + # Urls + for entity in entities['urls']: + start, end = entity['indices'][0], entity['indices'][1] + if use_display_url and entity.get('display_url'): + shown_url = entity['display_url'] + elif use_expanded_url and entity.get('expanded_url'): + shown_url = entity['expanded_url'] + else: + shown_url = entity['url'] + + url_html = '%s' + text = text.replace(tweet['text'][start:end], url_html % (entity['url'], shown_url)) + + return text From 3c637ddc7d19cd47cc3432892cfe72b067de57c1 Mon Sep 17 00:00:00 2001 From: Mike Helmick Date: Thu, 27 Jun 2013 19:20:43 -0400 Subject: [PATCH 2/2] Tests and documentation --- docs/index.rst | 1 + docs/usage/special_functions.rst | 47 ++++++++++++++++++++++++++++++++ docs/usage/starting_out.rst | 1 + tests/config.py | 3 ++ tests/test_core.py | 8 +++++- twython/api.py | 14 ++++++++-- 6 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 docs/usage/special_functions.rst diff --git a/docs/index.rst b/docs/index.rst index 5fa8848..e971e45 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -37,6 +37,7 @@ Usage usage/basic_usage usage/advanced_usage usage/streaming_api + usage/special_functions Twython API Documentation ------------------------- diff --git a/docs/usage/special_functions.rst b/docs/usage/special_functions.rst new file mode 100644 index 0000000..3d791b6 --- /dev/null +++ b/docs/usage/special_functions.rst @@ -0,0 +1,47 @@ +.. special-functions: + +Special Functions +================= + +This section covers methods to are part of Twython but not necessarily connected to the Twitter API. + +******************************************************************************* + +HTML for Tweet +-------------- + +This function takes a tweet object received from the Twitter API and returns an string formatted in HTML with the links, user mentions and hashtags replaced. + +.. code-block:: python + + from twython import Twython + + twitter = Twython(APP_KEY, APP_SECRET, + OAUTH_TOKEN, OAUTH_TOKEN_SECRET) + + user_tweets = twitter.get_user_timeline(screen_name='mikehelmick', + include_rts=True) + for tweet in user_tweets: + tweet['text'] = Twython.html_for_tweet(tweet) + print tweet['text'] + +The above code takes all the tweets from a specific users timeline, loops over them and replaces the value of ``tweet['text']`` with HTML. + +So: + + http://t.co/FCmXyI6VHd is a #cool site, lol! @mikehelmick should #checkitout. If you can! #thanks Love, @__twython__ https://t.co/67pwRvY6z9 + +will be replaced with: + + google.com is a #cool site, lol! @mikehelmick should #checkitout. If you can! #thanks Love, @__twython__ github.com + +.. note:: When converting the string to HTML we add a class to each HTML tag so that you can maninpulate the DOM later on. + +- For urls that are replaced we add ``class="twython-url"`` to the anchor tag +- For user mentions that are replaced we add ``class="twython-mention"`` to the anchor tag +- For hashtags that are replaced we add ``class="twython-hashtag"`` to the anchor tag + +This function excepts two parameters: ``use_display_url`` and ``use_expanded_url`` +By default, ``use_display_url`` is ``True``. Meaning the link displayed in the tweet text will appear as (ex. google.com, github.com) +If ``use_expanded_url`` is ``True``, it overrides ``use_display_url``. The urls will then be displayed as (ex. http://google.com, https://github.com) +If ``use_display_url`` and ``use_expanded_url`` is ``False``, short url will be used (t.co/xxxxx) \ No newline at end of file diff --git a/docs/usage/starting_out.rst b/docs/usage/starting_out.rst index 076e9fa..15a5dc8 100644 --- a/docs/usage/starting_out.rst +++ b/docs/usage/starting_out.rst @@ -81,6 +81,7 @@ You'll want to extract the ``oauth_verifier`` from the url. Django example: .. code-block:: python + oauth_verifier = request.GET['oauth_verifier'] Now that you have the ``oauth_verifier`` stored to a variable, you'll want to create a new instance of Twython and grab the final user tokens diff --git a/tests/config.py b/tests/config.py index f927d04..af098cd 100644 --- a/tests/config.py +++ b/tests/config.py @@ -17,3 +17,6 @@ protected_twitter_2 = os.environ.get('PROTECTED_TWITTER_2', 'TwythonSecure2') # Test Ids test_tweet_id = os.environ.get('TEST_TWEET_ID', '318577428610031617') test_list_id = os.environ.get('TEST_LIST_ID', '574') # 574 is @twitter/team + +test_tweet_object = {u'contributors': None, u'truncated': False, u'text': u'http://t.co/FCmXyI6VHd is a #cool site, lol! @mikehelmick should #checkitout. If you can! #thanks Love, @__twython__ https://t.co/67pwRvY6z9', u'in_reply_to_status_id': None, u'id': 349683012054683648, u'favorite_count': 0, u'source': u'web', u'retweeted': False, u'coordinates': None, u'entities': {u'symbols': [], u'user_mentions': [{u'id': 29251354, u'indices': [45, 57], u'id_str': u'29251354', u'screen_name': u'mikehelmick', u'name': u'Mike Helmick'}, {u'id': 1431865928, u'indices': [104, 116], u'id_str': u'1431865928', u'screen_name': u'__twython__', u'name': u'Twython'}], u'hashtags': [{u'indices': [28, 33], u'text': u'cool'}, {u'indices': [65, 76], u'text': u'checkitout'}, {u'indices': [90, 97], u'text': u'thanks'}], u'urls': [{u'url': u'http://t.co/FCmXyI6VHd', u'indices': [0, 22], u'expanded_url': u'http://google.com', u'display_url': u'google.com'}, {u'url': u'https://t.co/67pwRvY6z9', u'indices': [117, 140], u'expanded_url': u'https://github.com', u'display_url': u'github.com'}]}, u'in_reply_to_screen_name': None, u'id_str': u'349683012054683648', u'retweet_count': 0, u'in_reply_to_user_id': None, u'favorited': False, u'user': {u'follow_request_sent': False, u'profile_use_background_image': True, u'default_profile_image': True, u'id': 1431865928, u'verified': False, u'profile_text_color': u'333333', u'profile_image_url_https': u'https://si0.twimg.com/sticky/default_profile_images/default_profile_3_normal.png', u'profile_sidebar_fill_color': u'DDEEF6', u'entities': {u'description': {u'urls': []}}, u'followers_count': 1, u'profile_sidebar_border_color': u'C0DEED', u'id_str': u'1431865928', u'profile_background_color': u'3D3D3D', u'listed_count': 0, u'profile_background_image_url_https': u'https://si0.twimg.com/images/themes/theme1/bg.png', u'utc_offset': None, u'statuses_count': 2, u'description': u'', u'friends_count': 1, u'location': u'', u'profile_link_color': u'0084B4', u'profile_image_url': u'http://a0.twimg.com/sticky/default_profile_images/default_profile_3_normal.png', u'following': False, u'geo_enabled': False, u'profile_background_image_url': u'http://a0.twimg.com/images/themes/theme1/bg.png', u'screen_name': u'__twython__', u'lang': u'en', u'profile_background_tile': False, u'favourites_count': 0, u'name': u'Twython', u'notifications': False, u'url': None, u'created_at': u'Thu May 16 01:11:09 +0000 2013', u'contributors_enabled': False, u'time_zone': None, u'protected': False, u'default_profile': False, u'is_translator': False}, u'geo': None, u'in_reply_to_user_id_str': None, u'possibly_sensitive': False, u'lang': u'en', u'created_at': u'Wed Jun 26 00:18:21 +0000 2013', u'in_reply_to_status_id_str': None, u'place': None} +test_tweet_html = 'google.com is a #cool site, lol! @mikehelmick should #checkitout. If you can! #thanks Love, @__twython__ github.com' diff --git a/tests/test_core.py b/tests/test_core.py index d4ddfb3..1eff01e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -3,7 +3,8 @@ from twython import Twython, TwythonError, TwythonAuthError from .config import ( app_key, app_secret, oauth_token, oauth_token_secret, protected_twitter_1, protected_twitter_2, screen_name, - test_tweet_id, test_list_id, access_token + test_tweet_id, test_list_id, access_token, test_tweet_object, + test_tweet_html ) import time @@ -82,6 +83,11 @@ class TwythonAPITestCase(unittest.TestCase): """Test encoding UTF-8 works""" self.api.encode('Twython is awesome!') + def test_html_for_tweet(self): + """Test HTML for Twitter returns what we want""" + tweet_text = self.api.html_for_tweet(test_tweet_object) + self.assertEqual(test_tweet_html, tweet_text) + # Timelines def test_get_mentions_timeline(self): """Test returning mentions timeline for authenticated user succeeds""" diff --git a/twython/api.py b/twython/api.py index c67868f..c4dd59a 100644 --- a/twython/api.py +++ b/twython/api.py @@ -410,6 +410,16 @@ class Twython(EndpointsMixin, object): @staticmethod def html_for_tweet(tweet, use_display_url=True, use_expanded_url=False): + """Return HTML for a tweet (urls, mentions, hashtags replaced with links) + + :param tweet: Tweet object from received from Twitter API + :param use_display_url: Use display URL to represent link (ex. google.com, github.com). Default: True + :param use_expanded_url: Use expanded URL to represent link (e.g. http://google.com). Default False + + If use_expanded_url is True, it overrides use_display_url. + If use_display_url and use_expanded_url is False, short url will be used (t.co/xxxxx) + + """ if 'retweeted_status' in tweet: tweet = tweet['retweeted_status'] @@ -422,7 +432,7 @@ class Twython(EndpointsMixin, object): start, end = entity['indices'][0], entity['indices'][1] mention_html = '@%(screen_name)s' - text = text.replace(tweet['text'][start:end], mention_html % {'url': entity['screen_name']}) + text = text.replace(tweet['text'][start:end], mention_html % {'screen_name': entity['screen_name']}) # Hashtags for entity in entities['hashtags']: @@ -434,7 +444,7 @@ class Twython(EndpointsMixin, object): # Urls for entity in entities['urls']: start, end = entity['indices'][0], entity['indices'][1] - if use_display_url and entity.get('display_url'): + if use_display_url and entity.get('display_url') and not use_expanded_url: shown_url = entity['display_url'] elif use_expanded_url and entity.get('expanded_url'): shown_url = entity['expanded_url']