You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streams.apache.org by sb...@apache.org on 2014/07/21 06:49:16 UTC
[01/18] bootstrap of module
Repository: incubator-streams
Updated Branches:
refs/heads/master b7e4c3467 -> 7c5e29007
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/test/resources/testtweets.txt
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/resources/testtweets.txt b/streams-contrib/streams-provider-instagram/src/test/resources/testtweets.txt
new file mode 100644
index 0000000..6cb73b6
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/test/resources/testtweets.txt
@@ -0,0 +1,695 @@
+{"contributors":null,"text":"@JasonCalacanis Blade Runner is my favorite movie of all time. The \"Final Cut\" in HD/BluRay is amazing!","geo":null,"retweeted":false,"in_reply_to_screen_name":"Jason","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":1004,"name":"JasonMC","indices":[0,15],"screen_name":"Jasoncalacanis","id_str":"1004"}]},"in_reply_to_status_id_str":"801450173","id":801455863,"source":"web","in_reply_to_user_id_str":"3840","favorited":false,"in_reply_to_status_id":801450173,"retweet_count":0,"created_at":"Thu May 01 20:53:44 +0000 2008","in_reply_to_user_id":3840,"favorite_count":0,"id_str":"801455863","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology e
xecutive, and an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFE
C","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Installed fish finder on my kayak over the weekend.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":803894383,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Mon May 05 14:32:28 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"803894383","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","pro
file_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"
is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Flying (back) to Boston today.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":804623799,"source":"<a href=\"http://www.32hours.com/betwitteredinfo/\" rel=\"nofollow\">BeTwittered<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue May 06 12:31:14 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"804623799","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_side
bar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images
/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Why can't I see my tweets in Twinkle?! Is anyone else having issues with Twinkle since the last update a few days ago?","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":804863183,"source":"<a href=\"http://twitter.com/twinkleking\" rel=\"nofollow\">Twinkle<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue May 06 17:52:40 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"804863183","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologet
ic techno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD"
,"id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"In South Florida for two days of meetings.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":822771775,"source":"<a href=\"http://twitter.com/twinkleking\" rel=\"nofollow\">Twinkle<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu May 29 20:18:52 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"822771775","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_sideb
ar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/
1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"If you've never been to the Breakers in Palm Beach, I highly recommend it. The poolside cabanas are unbelievable!","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":837953391,"source":"<a href=\"http://twitter.com/twinkleking\" rel=\"nofollow\">Twinkle<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Wed Jun 18 17:22:39 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"837953391","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic te
chno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_
str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Leaving for Majorca, Spain to speak at a health care informatics conference.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":842661503,"source":"<a href=\"http://twitter.com/twinkleking\" rel=\"nofollow\">Twinkle<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue Jun 24 18:32:13 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"842661503","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic techno-geek.","verified":false,"contribu
tors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"ht
tp://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"iPhone firmware 2.0 is buggy for me. ActiveSync deleting appointments from my Outlook calendar and some of the app store apps don't work!","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":856160351,"source":"<a href=\"http://twitterrific.com\" rel=\"nofollow\">Twitterrific<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Sat Jul 12 00:52:22 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"856160351","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, a
nd an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_
name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@Paulflevy Your special contribution is Transparency as a foundation for safety/quality. Give some fresh cuts from your unique gift there.","geo":null,"retweeted":false,"in_reply_to_screen_name":"Paulflevy","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":17668347,"name":"Paul Levy","indices":[0,10],"screen_name":"Paulflevy","id_str":"17668347"}]},"in_reply_to_status_id_str":"1042274173","id":1042786575,"source":"web","in_reply_to_user_id_str":"17668347","favorited":false,"in_reply_to_status_id":1042274173,"retweet_count":0,"created_at":"Sun Dec 07 01:25:27 +0000 2008","in_reply_to_user_id":17668347,"favorite_count":0,"id_str":"1042786575","place":null,"user":{"location":"Southwest Desert - Arizona USA","default_profile":false,"profile_background_tile":false,"statuses_count":83,"lang":"en","profile_link_color":"FF3300","id":16030226,"following":false,"protected":false,"favourites_count":6,"profile_text
_color":"333333","description":"MBA Physician - pathology, informatics and Lean continuous improvement","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"86A4A6","name":"Peter P Patterson MD","profile_background_color":"8E9770","created_at":"Thu Aug 28 18:00:06 +0000 2008","default_profile_image":false,"followers_count":229,"profile_image_url_https":"https://pbs.twimg.com/profile_images/723673074/P652-LTPP0652117307JCP-2_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme6/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme6/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":52,"profile_sidebar_fill_color":"A0B1C7","screen_name":"IDrPete","id_str":"16030226","profile_image_url":"http://pbs.twimg.com/pr
ofile_images/723673074/P652-LTPP0652117307JCP-2_normal.jpg","listed_count":4,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"I am enjoying some time off with the family. Hoping all of you have a safe and joyous holiday season.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1071035551,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Sun Dec 21 20:38:32 +0000 2008","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1071035551","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_bord
er_color":"A8C7F7","name":"Carlos M. Nunez M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730
845/laptop_stethoscope_normal.jpg","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@anitasamarth Clinovations web site seems to promise more than it can deliver. I would suggest toning down some of the rhetoric.","geo":null,"retweeted":false,"in_reply_to_screen_name":"anitasamarth","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":16257434,"name":"Anita Samarth","indices":[0,13],"screen_name":"anitasamarth","id_str":"16257434"}]},"in_reply_to_status_id_str":"1106542273","id":1106752071,"source":"web","in_reply_to_user_id_str":"16257434","favorited":false,"in_reply_to_status_id":1106542273,"retweet_count":0,"created_at":"Fri Jan 09 14:14:42 +0000 2009","in_reply_to_user_id":16257434,"favorite_count":0,"id_str":"1106752071","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_co
lor":"333333","description":"Semi-retired University of Michigan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)
","notifications":false,"profile_use_background_image":true,"friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"An academic pathologist, Dr. Tony Killeen, comments on how lab test reference ranges are created (http://twurl.cc/bxe)","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1111123095,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Sun Jan 11 16:13:35 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1111123095","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michigan Medical School pathologist & informatician. Author of clinical lab & pathology blog
called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","i
d_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"UnitedHealth Settles Suit with New York Attorney General Cuomo: I have posted a number of previous notes about t.. http://tinyurl.com/6u8q2x","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1118315383,"source":"<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Wed Jan 14 14:20:20 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1118315383","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michi
gan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"
friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Free Exercise and Nutrition Program Offered in Parks in Brazil: In a previous post, I discussed the very high co.. http://tinyurl.com/86v7kx","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1120906383,"source":"<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu Jan 15 13:20:26 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1120906383","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michi
gan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"
friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Extracting Value from Twitter Messages Using Filters: As a relatively recent convert to Twitter, a microblogging.. http://tinyurl.com/73qchn","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1133231679,"source":"<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue Jan 20 14:24:17 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1133231679","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michi
gan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"
friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Book Premium for Early Registrants to Lab InfoTech Summit: We are offering a book premium to the first 100 regis.. http://tinyurl.com/c5kpfb","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1152789015,"source":"<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue Jan 27 18:23:33 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1152789015","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michi
gan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"
friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Anxiously awaiting the arrival of my new netbook...tomorrow.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1160434255,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Fri Jan 30 01:59:23 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1160434255","place":null,"user":{"location":"United States","default_profile":false,"profile_background_tile":false,"statuses_count":160,"lang":"en","profile_link_color":"0084B4","id":3569041,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"I am a physician, medical technology executive, and an unapologetic techno-geek.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"A8C7F7","name":"Carlos M. Nunez
M.D.","profile_background_color":"022330","created_at":"Thu Apr 05 23:48:44 +0000 2007","default_profile_image":false,"followers_count":180,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme15/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme15/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://goo.gl/MBeHL","indices":[0,22],"display_url":"goo.gl/MBeHL","url":"http://t.co/H2xSBV5AfY"}]}},"url":"http://t.co/H2xSBV5AfY","utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":26,"profile_sidebar_fill_color":"C0DFEC","screen_name":"CNunezMD","id_str":"3569041","profile_image_url":"http://pbs.twimg.com/profile_images/1829730845/laptop_stethoscope_normal.jpg","listed
_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@jenmccabegorman Lab information systems (LISs) manage manage about 80% of all health information. Also exploring digital pathology.","geo":null,"retweeted":false,"in_reply_to_screen_name":"jensmccabe","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":"1176600518","id":1177499983,"source":"web","in_reply_to_user_id_str":"14258044","favorited":false,"in_reply_to_status_id":1176600518,"retweet_count":0,"created_at":"Wed Feb 04 20:40:32 +0000 2009","in_reply_to_user_id":14258044,"favorite_count":0,"id_str":"1177499983","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michigan Medical School pathologist & informa
tician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":242,"profile_sidebar_fill
_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@scottshreeve If large health systems endorse home health physiologic monitoring, it can succeed -- they can provide needed infrastructure.","geo":null,"retweeted":false,"in_reply_to_screen_name":"scottshreeve","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":16059834,"name":"Scott Shreeve, MD","indices":[0,13],"screen_name":"scottshreeve","id_str":"16059834"}]},"in_reply_to_status_id_str":"1183578679","id":1184110279,"source":"web","in_reply_to_user_id_str":"16059834","favorited":false,"in_reply_to_status_id":1183578679,"retweet_count":0,"created_at":"Fri Feb 06 19:00:43 +0000 2009","in_reply_to_user_id":16059834,"favorite_count":0,"id_str":"1184110279","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"
profile_text_color":"333333","description":"Semi-retired University of Michigan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Tim
e (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Live Tweeting from Molecular Summit. For coronary artery stenosis dx, moving from lumen-ology to plaque-ology.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1195927703,"source":"<a href=\"http://twitter.com/devices\" rel=\"nofollow\">txt<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue Feb 10 16:30:30 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1195927703","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michigan Medical School pathologist & i
nformatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":242,"profile_sideba
r_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Live Tweeting from Molecular Summit. Jared Schwartz: Many blockbuster drugs show limited efficacy in as many as 70% of pts.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1197125583,"source":"<a href=\"http://twitter.com/devices\" rel=\"nofollow\">txt<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue Feb 10 22:35:27 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1197125583","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michigan Medical School pa
thologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":242,"p
rofile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Diagosed today: Unusual presentation. Pt w/ 1st symptom being visual disturbance. Brain biopsy of primary visual cortex: metastatic lung ca.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1197925487,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Wed Feb 11 03:34:13 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1197925487","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_count":247,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"F2E195","nam
e":"Brian Moore","profile_background_color":"BADFCD","created_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":523,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name":"neuropathology","id_str":"19749157","profile_image_url":"http://pbs.t
wimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","listed_count":27,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"NPR reports on challenge of switching to digital medical records: http://tinyurl.com/cl4a4p","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1216667127,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Mon Feb 16 21:20:41 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1216667127","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_count":247,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"F2E195","name":"Brian Moore","profile_background_color":"BADF
CD","created_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":523,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name":"neuropathology","id_str":"19749157","profile_image_url":"http://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10
_June_2011_for_twitter_normal.jpg","listed_count":27,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"just signed up for twitter","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1225092359,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu Feb 19 01:23:25 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1225092359","place":null,"user":{"location":"Houston, TX","default_profile":true,"profile_background_tile":false,"statuses_count":2,"lang":"en","profile_link_color":"0084B4","id":21263297,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"Ultrarunner, marathoner, neuropathologist","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Kent Heck","profile_background_color":"C0DEED","created_at":"Thu Feb 19 01:20:47 +0000 2009"
,"default_profile_image":true,"followers_count":14,"profile_image_url_https":"https://abs.twimg.com/sticky/default_profile_images/default_profile_6_normal.png","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":null,"time_zone":null,"notifications":false,"profile_use_background_image":true,"friends_count":17,"profile_sidebar_fill_color":"DDEEF6","screen_name":"nemaline","id_str":"21263297","profile_image_url":"http://abs.twimg.com/sticky/default_profile_images/default_profile_6_normal.png","listed_count":0,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Twitted that is , sorry and good night","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1225678863,"source":"<a href=\"http://m.twitter.com/\" rel=\"nofollow\">mobile web<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu Feb 19 04:53:54 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1225678863","place":null,"user":{"location":"Seattle","default_profile":false,"profile_background_tile":false,"statuses_count":194,"lang":"en","profile_link_color":"1F98C7","id":21274091,"following":false,"protected":false,"favourites_count":49,"profile_text_color":"663B12","description":"Christian seeker. Loving husband. Proud father. Practicing physician (surgical pathology, hematopathology), Musician (pianist). Chess player. Dem. Apple"
,"verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C6E2EE","name":"Charles Simrell","profile_background_color":"C6E2EE","created_at":"Thu Feb 19 04:01:18 +0000 2009","default_profile_image":false,"followers_count":17,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1173141302/image_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme2/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme2/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":94,"profile_sidebar_fill_color":"DAECF4","screen_name":"ChuckSimrell","id_str":"21274091","profile_image_url":"http://pbs.twimg.com/profile_images/1173141302/image_normal.jpg","listed_count":0,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Soup is done, ready for tomorrow.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1235740735,"source":"<a href=\"http://m.twitter.com/\" rel=\"nofollow\">mobile web<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Sun Feb 22 00:24:52 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1235740735","place":null,"user":{"location":"Seattle","default_profile":false,"profile_background_tile":false,"statuses_count":194,"lang":"en","profile_link_color":"1F98C7","id":21274091,"following":false,"protected":false,"favourites_count":49,"profile_text_color":"663B12","description":"Christian seeker. Loving husband. Proud father. Practicing physician (surgical pathology, hematopathology), Musician (pianist). Chess player. Dem. Apple","veri
fied":false,"contributors_enabled":false,"profile_sidebar_border_color":"C6E2EE","name":"Charles Simrell","profile_background_color":"C6E2EE","created_at":"Thu Feb 19 04:01:18 +0000 2009","default_profile_image":false,"followers_count":17,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1173141302/image_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme2/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme2/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":94,"profile_sidebar_fill_color":"DAECF4","screen_name":"ChuckSimrell","id_str":"21274091","profile_image_url":"http://pbs.twimg.com/profile_images/1173141302/image_normal.jpg","listed_count":0,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Just finished clean up from feeding 160+ church folk and homeless. Very good gathering.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1238561103,"source":"<a href=\"http://m.twitter.com/\" rel=\"nofollow\">mobile web<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Sun Feb 22 23:14:50 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1238561103","place":null,"user":{"location":"Seattle","default_profile":false,"profile_background_tile":false,"statuses_count":194,"lang":"en","profile_link_color":"1F98C7","id":21274091,"following":false,"protected":false,"favourites_count":49,"profile_text_color":"663B12","description":"Christian seeker. Loving husband. Proud father. Practicing physician (surgical pathology, hematopathology
), Musician (pianist). Chess player. Dem. Apple","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C6E2EE","name":"Charles Simrell","profile_background_color":"C6E2EE","created_at":"Thu Feb 19 04:01:18 +0000 2009","default_profile_image":false,"followers_count":17,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1173141302/image_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme2/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme2/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":-28800,"time_zone":"Pacific Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":94,"profile_sidebar_fill_color":"DAECF4","screen_name":"ChuckSimrell","id_str":"21274091","profile_image_url":"http://pbs.twimg.com/profile_images/1173141302/image_normal.jpg","listed_count":0,"is_translator
":false},"coordinates":null}
+{"contributors":null,"text":"I guess I'm an official \"TWIT\" now.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1241067119,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Mon Feb 23 15:16:03 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1241067119","place":null,"user":{"location":"Northeast Kingdom of VT","default_profile":false,"profile_background_tile":false,"statuses_count":200,"lang":"en","profile_link_color":"0084B4","id":21651866,"following":false,"protected":false,"favourites_count":3,"profile_text_color":"333333","description":"Feral pathologist. (In the wild...)","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"FFFFFF","name":"Robert W. McDowell","profile_background_color":"9AE4E8","created_at":"Mo
n Feb 23 13:26:56 +0000 2009","default_profile_image":false,"followers_count":14,"profile_image_url_https":"https://pbs.twimg.com/profile_images/531893711/100_3967a_normal.JPG","geo_enabled":true,"profile_background_image_url":"http://a0.twimg.com/profile_background_images/378800000138452440/NG96U6hr.jpeg","profile_background_image_url_https":"https://si0.twimg.com/profile_background_images/378800000138452440/NG96U6hr.jpeg","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":12,"profile_sidebar_fill_color":"DDFFCC","screen_name":"msie01","id_str":"21651866","profile_image_url":"http://pbs.twimg.com/profile_images/531893711/100_3967a_normal.JPG","listed_count":3,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@UMHealthSystem Community benefit. I am still timing out on server www2.med.umich.edu 24 hours later.","geo":null,"retweeted":false,"in_reply_to_screen_name":"UMHealthSystem","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":15457383,"name":"U-M Health System","indices":[0,15],"screen_name":"UMHealthSystem","id_str":"15457383"}]},"in_reply_to_status_id_str":"1241366607","id":1243544607,"source":"web","in_reply_to_user_id_str":"15457383","favorited":false,"in_reply_to_status_id":1241366607,"retweet_count":0,"created_at":"Tue Feb 24 03:40:14 +0000 2009","in_reply_to_user_id":15457383,"favorite_count":0,"id_str":"1243544607","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","desc
ription":"Semi-retired University of Michigan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":f
alse,"profile_use_background_image":true,"friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@hiphopoptamus Korean named president of Dartmouth College: http://www.dartmouth.edu/presidentelect/","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":129898249,"name":"KingAdobo","indices":[0,14],"screen_name":"Hiphopoptamus","id_str":"129898249"}]},"in_reply_to_status_id_str":null,"id":1275422599,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Tue Mar 03 20:37:14 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1275422599","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_count":247,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"contribut
ors_enabled":false,"profile_sidebar_border_color":"F2E195","name":"Brian Moore","profile_background_color":"BADFCD","created_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":523,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name":"neurop
athology","id_str":"19749157","profile_image_url":"http://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","listed_count":27,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@cosmeticclinic Tummy Tuck 101: http://tinyurl.com/cnyjdu._Lee","geo":null,"retweeted":false,"in_reply_to_screen_name":"cosmeticclinic","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":22731442,"name":"Mississauga Cosmetic","indices":[0,15],"screen_name":"cosmeticclinic","id_str":"22731442"}]},"in_reply_to_status_id_str":"1277166229","id":1278479255,"source":"web","in_reply_to_user_id_str":"22731442","favorited":false,"in_reply_to_status_id":1277166229,"retweet_count":0,"created_at":"Wed Mar 04 13:43:27 +0000 2009","in_reply_to_user_id":22731442,"favorite_count":0,"id_str":"1278479255","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_count":247,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"c
ontributors_enabled":false,"profile_sidebar_border_color":"F2E195","name":"Brian Moore","profile_background_color":"BADFCD","created_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":523,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name"
:"neuropathology","id_str":"19749157","profile_image_url":"http://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","listed_count":27,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"RT @jamesbyers: RT @CRAIGLSANDERS \"One of the greatest success secrets is uninterrupted time given to a specific task.\" - Steve Chandler","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":17050074,"name":"James Byers","indices":[3,14],"screen_name":"jamesbyers","id_str":"17050074"},{"id":20658282,"name":"Craig L. Sanders","indices":[19,33],"screen_name":"CRAIGLSANDERS","id_str":"20658282"}]},"in_reply_to_status_id_str":null,"id":1279318623,"source":"<a href=\"http://www.tweetdeck.com/\" rel=\"nofollow\">TweetDeck<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Wed Mar 04 17:09:24 +0000 2009","in_reply_to_user_id":null,"favorite_count":1,"id_str":"1279318623","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_co
unt":247,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"F2E195","name":"Brian Moore","profile_background_color":"BADFCD","created_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":523,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.
co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name":"neuropathology","id_str":"19749157","profile_image_url":"http://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","listed_count":27,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Using an iPhone for Review or Diagnosis of Pathology Images: In a recent blog note (see: iPhone, the Fifth Most .. http://tinyurl.com/aszn4f","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1283235239,"source":"<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu Mar 05 13:26:18 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1283235239","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michi
gan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"
friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"@JayOatway Totally agree. One other reason. Twitter positioning itself to supply local news linked to revenue-generating classifieds.","geo":null,"retweeted":false,"in_reply_to_screen_name":"JayOatway","truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[{"id":616173,"name":"Jay Oatway","indices":[0,10],"screen_name":"JayOatway","id_str":"616173"}]},"in_reply_to_status_id_str":"1306339770","id":1306463087,"source":"web","in_reply_to_user_id_str":"616173","favorited":false,"in_reply_to_status_id":1306339770,"retweet_count":0,"created_at":"Tue Mar 10 17:56:46 +0000 2009","in_reply_to_user_id":616173,"favorite_count":0,"id_str":"1306463087","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333
","description":"Semi-retired University of Michigan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notificat
ions":false,"profile_use_background_image":true,"friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Few Vendors Deliver an Integrated RIS/PACS Solution According to KLAS: I have been a strong advocate for the mer.. http://tinyurl.com/cpu5nj","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1310116663,"source":"<a href=\"http://twitterfeed.com\" rel=\"nofollow\">twitterfeed<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Wed Mar 11 11:28:55 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1310116663","place":null,"user":{"location":"Ann Arbor, Michigan, USA.","default_profile":true,"profile_background_tile":false,"statuses_count":1440,"lang":"en","profile_link_color":"0084B4","id":18653047,"following":false,"protected":false,"favourites_count":14,"profile_text_color":"333333","description":"Semi-retired University of Michi
gan Medical School pathologist & informatician. Author of clinical lab & pathology blog called Lab Soft News.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bruce Friedman","profile_background_color":"C0DEED","created_at":"Tue Jan 06 00:16:41 +0000 2009","default_profile_image":false,"followers_count":890,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://www.labsoftnews.com","indices":[0,22],"display_url":"labsoftnews.com","url":"http://t.co/9wOtT2Lwcm"}]}},"url":"http://t.co/9wOtT2Lwcm","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"
friends_count":242,"profile_sidebar_fill_color":"DDEEF6","screen_name":"labsoftnews","id_str":"18653047","profile_image_url":"http://pbs.twimg.com/profile_images/1120324523/baf_normal.jpg","listed_count":29,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Why lucid interval after epidural hematoma trauma? Initial blow causes concussion. Pt wakes. Bleed pressure: loss of consciousness again.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1321360735,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Fri Mar 13 12:23:12 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1321360735","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_count":247,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"F2E195","name
":"Brian Moore","profile_background_color":"BADFCD","created_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":523,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name":"neuropathology","id_str":"19749157","profile_image_url":"http://pbs.tw
img.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","listed_count":27,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Finishing a gel.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1324179463,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Fri Mar 13 21:51:44 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1324179463","place":null,"user":{"location":"","default_profile":true,"profile_background_tile":false,"statuses_count":3,"lang":"en","profile_link_color":"0084B4","id":24262686,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bradley Miller","profile_background_color":"C0DEED","created_at":"Fri Mar 13 21:50:32 +0000 2009","default_profile_image":true,"followers_count":6,"profil
e_image_url_https":"https://abs.twimg.com/sticky/default_profile_images/default_profile_0_normal.png","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":null,"time_zone":null,"notifications":false,"profile_use_background_image":true,"friends_count":1,"profile_sidebar_fill_color":"DDEEF6","screen_name":"miller2865","id_str":"24262686","profile_image_url":"http://abs.twimg.com/sticky/default_profile_images/default_profile_0_normal.png","listed_count":0,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Reviewing a case. Just did a frozen. Going back to the dentist today","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1388548023,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Wed Mar 25 15:39:16 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1388548023","place":null,"user":{"location":"Indiana/ North Carolina","default_profile":false,"profile_background_tile":false,"statuses_count":487,"lang":"en","profile_link_color":"9EDEF5","id":26316026,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"333333","description":"I am an American Forensic Pathologist, and Fitness Activist, who likes to write and race bicycles.","verified":false,"contributors_enabled":false,"profile_sidebar_border_c
olor":"9CEE72","name":"Scott Wagner M.D.","profile_background_color":"C9FAFD","created_at":"Tue Mar 24 20:29:03 +0000 2009","default_profile_image":false,"followers_count":85,"profile_image_url_https":"https://pbs.twimg.com/profile_images/139026389/At_work2.1_normal.JPG","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://Pathfor.com","indices":[0,22],"display_url":"Pathfor.com","url":"http://t.co/ckrct3sKAd"}]}},"url":"http://t.co/ckrct3sKAd","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":239,"profile_sidebar_fill_color":"EFFFCC","screen_name":"sawagnermd","id_str":"26316026","profile_image_url":"http://pbs.twimg.com/profile_images/139026389/At_work2.1_nor
mal.JPG","listed_count":2,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Thinking about sleep.","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1391418135,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu Mar 26 00:27:06 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1391418135","place":null,"user":{"location":"","default_profile":true,"profile_background_tile":false,"statuses_count":3,"lang":"en","profile_link_color":"0084B4","id":24262686,"following":false,"protected":false,"favourites_count":0,"profile_text_color":"333333","description":"","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"C0DEED","name":"Bradley Miller","profile_background_color":"C0DEED","created_at":"Fri Mar 13 21:50:32 +0000 2009","default_profile_image":true,"followers_count":6,"p
rofile_image_url_https":"https://abs.twimg.com/sticky/default_profile_images/default_profile_0_normal.png","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]}},"url":null,"utc_offset":null,"time_zone":null,"notifications":false,"profile_use_background_image":true,"friends_count":1,"profile_sidebar_fill_color":"DDEEF6","screen_name":"miller2865","id_str":"24262686","profile_image_url":"http://abs.twimg.com/sticky/default_profile_images/default_profile_0_normal.png","listed_count":0,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"Mayo Clinic Medical Education Reform Summit April 26-28 http://tinyurl.com/dj3uwl","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1391798423,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Thu Mar 26 01:41:14 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1391798423","place":null,"user":{"location":"Springfield, IL","default_profile":false,"profile_background_tile":false,"statuses_count":248,"lang":"en","profile_link_color":"FF0000","id":19749157,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"0C3E53","description":"Neuropathologist","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"F2E195","name":"Brian Moore","profile_background_color":"BADFCD","creat
ed_at":"Fri Jan 30 02:11:17 +0000 2009","default_profile_image":false,"followers_count":526,"profile_image_url_https":"https://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011_for_twitter_normal.jpg","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme12/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme12/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://neuropathologyblog.blogspot.com","indices":[0,22],"display_url":"neuropathologyblog.blogspot.com","url":"http://t.co/2xdql9mCYM"}]}},"url":"http://t.co/2xdql9mCYM","utc_offset":-21600,"time_zone":"Central Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":41,"profile_sidebar_fill_color":"FFF7CC","screen_name":"neuropathology","id_str":"19749157","profile_image_url":"http://pbs.twimg.com/profile_images/1453301647/Moore_Brian_10_June_2011
_for_twitter_normal.jpg","listed_count":28,"is_translator":false},"coordinates":null}
+{"contributors":null,"text":"On call...first case delayed already!","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1400537143,"source":"web","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Fri Mar 27 12:25:36 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1400537143","place":null,"user":{"location":"Indiana/ North Carolina","default_profile":false,"profile_background_tile":false,"statuses_count":487,"lang":"en","profile_link_color":"9EDEF5","id":26316026,"following":false,"protected":false,"favourites_count":4,"profile_text_color":"333333","description":"I am an American Forensic Pathologist, and Fitness Activist, who likes to write and race bicycles.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"9CEE72","name":"Scott Wag
ner M.D.","profile_background_color":"C9FAFD","created_at":"Tue Mar 24 20:29:03 +0000 2009","default_profile_image":false,"followers_count":85,"profile_image_url_https":"https://pbs.twimg.com/profile_images/139026389/At_work2.1_normal.JPG","geo_enabled":false,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme1/bg.png","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme1/bg.png","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"urls":[{"expanded_url":"http://Pathfor.com","indices":[0,22],"display_url":"Pathfor.com","url":"http://t.co/ckrct3sKAd"}]}},"url":"http://t.co/ckrct3sKAd","utc_offset":-18000,"time_zone":"Eastern Time (US & Canada)","notifications":false,"profile_use_background_image":true,"friends_count":239,"profile_sidebar_fill_color":"EFFFCC","screen_name":"sawagnermd","id_str":"26316026","profile_image_url":"http://pbs.twimg.com/profile_images/139026389/At_work2.1_normal.JPG","listed_count":2,"is_tr
anslator":false},"coordinates":null}
+{"contributors":null,"text":"Biked 12.00 miles\r\n in 1h 00m. Spinning Class","geo":null,"retweeted":false,"in_reply_to_screen_name":null,"truncated":false,"lang":"en","entities":{"symbols":[],"urls":[],"hashtags":[],"user_mentions":[]},"in_reply_to_status_id_str":null,"id":1402341623,"source":"<a href=\"http://www.beginnertriathlete.com\" rel=\"nofollow\">BeginnerTriathlete.com<\/a>","in_reply_to_user_id_str":null,"favorited":false,"in_reply_to_status_id":null,"retweet_count":0,"created_at":"Fri Mar 27 17:51:33 +0000 2009","in_reply_to_user_id":null,"favorite_count":0,"id_str":"1402341623","place":null,"user":{"location":"Seattle Area","default_profile":false,"profile_background_tile":false,"statuses_count":560,"lang":"en","profile_link_color":"088253","id":16283736,"following":false,"protected":false,"favourites_count":89,"profile_text_color":"634047","description":"Pathologist developing innovative digital pathology solutions to connect patients around the world with the highest
quality diagnosticians.","verified":false,"contributors_enabled":false,"profile_sidebar_border_color":"D3D2CF","name":"Gregory S Henderson","profile_background_color":"EDECE9","created_at":"Sun Sep 14 15:19:36 +0000 2008","default_profile_image":false,"followers_count":1349,"profile_image_url_https":"https://pbs.twimg.com/profile_images/339777194/Henderson-Gregory-IMG_4461B_normal.jpg","geo_enabled":true,"profile_background_image_url":"http://abs.twimg.com/images/themes/theme3/bg.gif","profile_background_image_url_https":"https://abs.twimg.com/images/themes/theme3/bg.gif","follow_request_sent":false,"entities":{"description":{"urls":[]},"url":{"url
<TRUNCATED>
[03/18] git commit: Added mapping:
Posted by sb...@apache.org.
Added mapping:
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/0df4fa1c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/0df4fa1c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/0df4fa1c
Branch: refs/heads/master
Commit: 0df4fa1c8477f559c154a71b5a9ea108c3daef3d
Parents: 537b97a
Author: mfranklin <mf...@apache.org>
Authored: Tue Jul 1 10:25:53 2014 -0400
Committer: mfranklin <mf...@apache.org>
Committed: Tue Jul 1 10:25:53 2014 -0400
----------------------------------------------------------------------
.../metadata/instagram_to_activity_mapping.png | Bin 0 -> 1123684 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/0df4fa1c/streams-contrib/streams-provider-instagram/metadata/instagram_to_activity_mapping.png
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/metadata/instagram_to_activity_mapping.png b/streams-contrib/streams-provider-instagram/metadata/instagram_to_activity_mapping.png
new file mode 100644
index 0000000..a3b7050
Binary files /dev/null and b/streams-contrib/streams-provider-instagram/metadata/instagram_to_activity_mapping.png differ
[14/18] git commit: STREAMS-121 | Merged apache instagram branch
Posted by sb...@apache.org.
STREAMS-121 | Merged apache instagram branch
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/1e233007
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/1e233007
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/1e233007
Branch: refs/heads/master
Commit: 1e233007d0adcff9459bda9bad8dea00e2f27be4
Parents: 95f6d5d 14f7050
Author: rebanks <re...@w2odigital.com>
Authored: Fri Jul 11 16:41:12 2014 -0500
Committer: rebanks <re...@w2odigital.com>
Committed: Fri Jul 11 16:41:12 2014 -0500
----------------------------------------------------------------------
streams-contrib/pom.xml | 1 +
.../processor/InstagramTypeConverter.java | 116 ++----------
.../InstagramJsonActivitySerializer.java | 22 ++-
.../serializer/util/InstagramActivityUtil.java | 187 ++++++++++++++++---
.../serializer/util/InstagramDeserializer.java | 33 ++++
.../test/InstagramActivitySerDeTest.java | 88 +++++++++
.../src/test/resources/testMediaFeedObjects.txt | 2 +
7 files changed, 323 insertions(+), 126 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/1e233007/streams-contrib/pom.xml
----------------------------------------------------------------------
[10/18] git commit: STREAMS-122 | Added header
Posted by sb...@apache.org.
STREAMS-122 | Added header
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/957e13f9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/957e13f9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/957e13f9
Branch: refs/heads/master
Commit: 957e13f9d0bcf330d40e47691ce2c9b8a038781a
Parents: 23bfcac
Author: Robert Douglas <rd...@w2odigital.com>
Authored: Wed Jul 2 11:19:57 2014 -0500
Committer: Robert Douglas <rd...@w2odigital.com>
Committed: Wed Jul 2 11:19:57 2014 -0500
----------------------------------------------------------------------
.../serializer/util/InstagramDeserializer.java | 21 +++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/957e13f9/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
index b4aee2d..d4280bf 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
@@ -1,11 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
package org.apache.streams.instagram.serializer.util;
import org.jinstagram.Instagram;
import org.jinstagram.exceptions.InstagramException;
-/**
- * Created by rdouglas on 7/1/14.
- */
public class InstagramDeserializer extends Instagram{
public InstagramDeserializer(String test) {
super(test);
[04/18] git commit: Adding MUP source
Posted by sb...@apache.org.
Adding MUP source
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/a3f443fe
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/a3f443fe
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/a3f443fe
Branch: refs/heads/master
Commit: a3f443fe76ee3ae99925c88b17a18954a3662edd
Parents: 0df4fa1
Author: mfranklin <mf...@apache.org>
Authored: Tue Jul 1 10:27:28 2014 -0400
Committer: mfranklin <mf...@apache.org>
Committed: Tue Jul 1 10:27:28 2014 -0400
----------------------------------------------------------------------
.../InstagramMediaDataActivitySerializer.mup | 894 +++++++++++++++++++
1 file changed, 894 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/a3f443fe/streams-contrib/streams-provider-instagram/metadata/InstagramMediaDataActivitySerializer.mup
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/metadata/InstagramMediaDataActivitySerializer.mup b/streams-contrib/streams-provider-instagram/metadata/InstagramMediaDataActivitySerializer.mup
new file mode 100644
index 0000000..deddd26
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/metadata/InstagramMediaDataActivitySerializer.mup
@@ -0,0 +1,894 @@
+{
+ "title": "InstagramMediaDataActivitySerializer",
+ "id": 1,
+ "formatVersion": 2,
+ "ideas": {
+ "1": {
+ "title": "activity",
+ "id": 2,
+ "ideas": {
+ "1": {
+ "title": "published",
+ "id": 25
+ },
+ "2": {
+ "title": "id:instagram:post:<id>",
+ "id": 26
+ },
+ "3": {
+ "title": "provider",
+ "id": 27,
+ "ideas": {
+ "1": {
+ "title": "id",
+ "id": 28,
+ "ideas": {
+ "1": {
+ "title": "id:providers:instagram",
+ "id": 31
+ }
+ }
+ },
+ "2": {
+ "title": "displayName",
+ "id": 29,
+ "ideas": {
+ "1": {
+ "title": "Instagram",
+ "id": 30
+ }
+ }
+ }
+ }
+ },
+ "6": {
+ "title": "url",
+ "id": 40
+ },
+ "7": {
+ "title": "object",
+ "id": 46,
+ "ideas": {
+ "1": {
+ "title": "objectType",
+ "id": 38,
+ "attr": {
+ "style": {}
+ }
+ },
+ "2": {
+ "title": "attachments",
+ "id": 51,
+ "ideas": {
+ "1": {
+ "title": "object",
+ "id": 52,
+ "ideas": {
+ "1": {
+ "title": "objectType",
+ "id": 53,
+ "ideas": {
+ "1": {
+ "title": "\"image\"",
+ "id": 81
+ }
+ }
+ },
+ "2": {
+ "title": "image",
+ "id": 54,
+ "ideas": {
+ "1": {
+ "title": "url",
+ "id": 55
+ },
+ "2": {
+ "title": "height",
+ "id": 56
+ },
+ "3": {
+ "title": "width",
+ "id": 57
+ }
+ }
+ }
+ }
+ },
+ "2": {
+ "title": "object",
+ "ideas": {
+ "1": {
+ "title": "objectType",
+ "ideas": {
+ "1": {
+ "title": "\"image\"",
+ "id": 101
+ }
+ },
+ "id": 100
+ },
+ "2": {
+ "title": "image",
+ "ideas": {
+ "1": {
+ "title": "url",
+ "id": 103
+ },
+ "2": {
+ "title": "height",
+ "id": 104
+ },
+ "3": {
+ "title": "width",
+ "id": 105
+ }
+ },
+ "id": 102
+ }
+ },
+ "id": 99,
+ "attr": {
+ "style": {}
+ }
+ },
+ "3": {
+ "title": "object",
+ "ideas": {
+ "1": {
+ "title": "objectType",
+ "ideas": {
+ "1": {
+ "title": "\"video\"",
+ "id": 108
+ }
+ },
+ "id": 107
+ },
+ "2": {
+ "title": "image",
+ "ideas": {
+ "1": {
+ "title": "url",
+ "id": 110
+ },
+ "2": {
+ "title": "height",
+ "id": 111
+ },
+ "3": {
+ "title": "width",
+ "id": 112
+ }
+ },
+ "id": 109
+ }
+ },
+ "id": 106,
+ "attr": {
+ "style": {}
+ }
+ }
+ }
+ },
+ "3": {
+ "title": "image\n\nIF objectType == video\nimage = data.videos.standard_resolution\nELSE \nimage = data.images.standard_resolution",
+ "id": 62,
+ "attr": {
+ "position": [
+ 74.5,
+ 74,
+ 1
+ ],
+ "style": {}
+ },
+ "ideas": {
+ "1": {
+ "title": "url",
+ "id": 64
+ },
+ "2": {
+ "title": "height",
+ "id": 65
+ },
+ "3": {
+ "title": "width",
+ "id": 66
+ }
+ }
+ },
+ "4": {
+ "title": "content",
+ "id": 113
+ }
+ }
+ },
+ "8": {
+ "title": "extensions",
+ "id": 75,
+ "ideas": {
+ "1": {
+ "title": "instagram",
+ "id": 91,
+ "attr": {
+ "style": {}
+ }
+ },
+ "2": {
+ "title": "location",
+ "id": 77,
+ "ideas": {
+ "1": {
+ "title": "coordinates",
+ "id": 78,
+ "ideas": {
+ "1": {
+ "title": "type",
+ "id": 79,
+ "ideas": {
+ "1": {
+ "title": "\"Point\"",
+ "id": 80
+ }
+ }
+ },
+ "0.5": {
+ "title": "coordinates\n\n[longitude,latitude]",
+ "id": 82,
+ "attr": {
+ "style": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ "12": {
+ "title": "hashtags",
+ "id": 76,
+ "attr": {
+ "position": [
+ 93.5,
+ 125.5,
+ 1
+ ],
+ "style": {}
+ }
+ },
+ "13": {
+ "title": "user_mentions",
+ "id": 92,
+ "ideas": {
+ "1": {
+ "title": "id",
+ "id": 93
+ },
+ "2": {
+ "title": "handle",
+ "id": 94
+ }
+ }
+ },
+ "9.5": {
+ "title": "rebroadcasts",
+ "id": 88,
+ "attr": {
+ "style": {}
+ }
+ },
+ "10.75": {
+ "title": "likes",
+ "id": 87,
+ "attr": {
+ "style": {}
+ },
+ "ideas": {
+ "1": {
+ "title": "count",
+ "id": 90
+ }
+ }
+ }
+ },
+ "attr": {}
+ },
+ "9": {
+ "title": "content",
+ "id": 114
+ },
+ "0.5": {
+ "title": "actor",
+ "id": 32,
+ "ideas": {
+ "1": {
+ "title": "id:instagram:<id>",
+ "id": 35
+ },
+ "2": {
+ "title": "image",
+ "id": 49,
+ "ideas": {
+ "1": {
+ "title": "url",
+ "id": 50
+ }
+ }
+ },
+ "0.5": {
+ "title": "handle",
+ "id": 36,
+ "attr": {
+ "position": [
+ 63.5,
+ -66,
+ 1
+ ],
+ "style": {}
+ }
+ },
+ "0.25": {
+ "title": "extensions",
+ "id": 47,
+ "ideas": {
+ "1": {
+ "title": "screenName",
+ "id": 48,
+ "attr": {
+ "style": {}
+ }
+ }
+ },
+ "attr": {
+ "position": [
+ 60.5,
+ -126.5,
+ 2
+ ],
+ "style": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ "-1": {
+ "title": "data",
+ "id": 4,
+ "ideas": {
+ "1": {
+ "title": "comments",
+ "id": 5,
+ "ideas": {
+ "1": {
+ "title": "data",
+ "id": 44
+ }
+ }
+ },
+ "3": {
+ "title": "caption",
+ "id": 7,
+ "ideas": {}
+ },
+ "4": {
+ "title": "links",
+ "id": 9
+ },
+ "5": {
+ "title": "link",
+ "id": 10
+ },
+ "6": {
+ "title": "user",
+ "id": 11,
+ "ideas": {
+ "1": {
+ "title": "username",
+ "id": 33
+ },
+ "2": {
+ "title": "profile_picture",
+ "id": 34
+ },
+ "3": {
+ "title": "id",
+ "id": 37
+ }
+ }
+ },
+ "9": {
+ "title": "type",
+ "id": 21
+ },
+ "12": {
+ "title": "videos",
+ "id": 41,
+ "ideas": {
+ "1": {
+ "title": "low_resolution",
+ "id": 42,
+ "ideas": {
+ "1": {
+ "title": "url",
+ "id": 67
+ },
+ "2": {
+ "title": "height",
+ "id": 68
+ },
+ "3": {
+ "title": "width",
+ "id": 69
+ }
+ }
+ },
+ "2": {
+ "title": "standard_resolution",
+ "id": 43,
+ "ideas": {
+ "1": {
+ "title": "height",
+ "id": 70
+ },
+ "2": {
+ "title": "width",
+ "id": 71
+ },
+ "3": {
+ "title": "url",
+ "id": 72
+ }
+ }
+ }
+ }
+ },
+ "13": {
+ "title": "tags",
+ "id": 45,
+ "ideas": {}
+ },
+ "14": {
+ "title": "users_in_photo",
+ "id": 95,
+ "ideas": {
+ "1": {
+ "title": "id",
+ "id": 96,
+ "ideas": {}
+ },
+ "2": {
+ "title": "profile_picture",
+ "id": 98
+ },
+ "3": {
+ "title": "username",
+ "id": 97,
+ "attr": {
+ "style": {}
+ }
+ }
+ }
+ },
+ "11.5": {
+ "title": "images",
+ "id": 14,
+ "ideas": {
+ "1": {
+ "title": "low_resolution",
+ "id": 15,
+ "ideas": {
+ "1": {
+ "title": "url",
+ "id": 18
+ },
+ "2": {
+ "title": "height",
+ "id": 58
+ },
+ "3": {
+ "title": "width",
+ "id": 59
+ }
+ }
+ },
+ "2": {
+ "title": "thumbnail",
+ "id": 16,
+ "ideas": {
+ "1": {
+ "title": "url",
+ "id": 19
+ },
+ "2": {
+ "title": "height",
+ "id": 60
+ },
+ "3": {
+ "title": "width",
+ "id": 61
+ }
+ }
+ },
+ "3": {
+ "title": "standard_resolution",
+ "id": 17,
+ "ideas": {
+ "1": {
+ "title": "url",
+ "id": 20
+ },
+ "2": {
+ "title": "height",
+ "id": 73
+ },
+ "3": {
+ "title": "width",
+ "id": 74
+ }
+ }
+ }
+ }
+ },
+ "6.5": {
+ "title": "id",
+ "id": 23
+ },
+ "6.25": {
+ "title": "created_time",
+ "id": 12
+ },
+ "12.5": {
+ "title": "likes",
+ "id": 6,
+ "ideas": {
+ "1": {
+ "title": "count",
+ "id": 89
+ }
+ },
+ "attr": {
+ "style": {}
+ }
+ },
+ "12.25": {
+ "title": "location",
+ "id": 24,
+ "ideas": {
+ "1": {
+ "title": "latitude",
+ "id": 83
+ },
+ "2": {
+ "title": "longitude",
+ "id": 84
+ }
+ },
+ "attr": {
+ "style": {}
+ }
+ }
+ }
+ }
+ },
+ "links": [
+ {
+ "ideaIdFrom": 23,
+ "ideaIdTo": 26,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 33,
+ "ideaIdTo": 36,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 37,
+ "ideaIdTo": 35,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 12,
+ "ideaIdTo": 25,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 21,
+ "ideaIdTo": 38,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 33,
+ "ideaIdTo": 48,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 50,
+ "ideaIdTo": 34,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 83,
+ "ideaIdTo": 82,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 84,
+ "ideaIdTo": 82,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 45,
+ "ideaIdTo": 76,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 89,
+ "ideaIdTo": 90,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 91,
+ "ideaIdTo": 4,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 40,
+ "ideaIdTo": 10,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 97,
+ "ideaIdTo": 94,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 96,
+ "ideaIdTo": 93,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 64,
+ "ideaIdTo": 20,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 65,
+ "ideaIdTo": 73,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 66,
+ "ideaIdTo": 74,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 66,
+ "ideaIdTo": 71,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 65,
+ "ideaIdTo": 70,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 64,
+ "ideaIdTo": 72,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 55,
+ "ideaIdTo": 19,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 56,
+ "ideaIdTo": 60,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 61,
+ "ideaIdTo": 57,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 103,
+ "ideaIdTo": 18,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 104,
+ "ideaIdTo": 58,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 59,
+ "ideaIdTo": 105,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 110,
+ "ideaIdTo": 67,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 111,
+ "ideaIdTo": 68,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 112,
+ "ideaIdTo": 69,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 7,
+ "ideaIdTo": 114,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ },
+ {
+ "ideaIdFrom": 113,
+ "ideaIdTo": 7,
+ "attr": {
+ "style": {
+ "color": "#FF0000",
+ "lineStyle": "dashed"
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
[02/18] git commit: bootstrap of module
Posted by sb...@apache.org.
bootstrap of module
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/537b97aa
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/537b97aa
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/537b97aa
Branch: refs/heads/master
Commit: 537b97aa0adb1bf396b51cc12214722bf4f95c6c
Parents: 34c95a6
Author: Steve Blackmon <sb...@w2odigital.com>
Authored: Thu Jun 26 18:58:14 2014 -0500
Committer: Steve Blackmon <sb...@w2odigital.com>
Committed: Thu Jun 26 18:58:14 2014 -0500
----------------------------------------------------------------------
.../streams-provider-instagram/README.md | 17 +
.../streams-provider-instagram/pom.xml | 139 ++++
.../instagram/InstagramConfigurator.java | 78 +++
.../processor/InstagramTypeConverter.java | 190 +++++
.../provider/InstagramTimelineProvider.java | 409 +++++++++++
.../InstagramJsonActivitySerializer.java | 60 ++
.../serializer/util/InstagramActivityUtil.java | 142 ++++
.../com/instagram/InstagramConfiguration.json | 20 +
.../InstagramUserInformationConfiguration.json | 17 +
.../src/main/resources/reference.conf | 5 +
.../test/InstagramActivitySerDeTest.java | 92 +++
.../src/test/resources/testtweets.txt | 695 +++++++++++++++++++
12 files changed, 1864 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/README.md
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/README.md b/streams-contrib/streams-provider-instagram/README.md
new file mode 100644
index 0000000..3bca23b
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/README.md
@@ -0,0 +1,17 @@
+streams-provider-instagram
+
+Purpose
+
+ Module connects to instagram API, collects events, converts to activity, and passes each activity downstream.
+
+Example configuration
+
+ "instagram": {
+ "version": "v1",
+ "endpoint": "media/recent",
+ "accessToken": "",
+ "info": [
+ "3",
+ "kevin"
+ ]
+ }
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/pom.xml
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/pom.xml b/streams-contrib/streams-provider-instagram/pom.xml
new file mode 100644
index 0000000..e356a7a
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/pom.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.apache.streams</groupId>
+ <artifactId>streams-contrib</artifactId>
+ <version>0.1-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>streams-provider-instagram</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.typesafe</groupId>
+ <artifactId>config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.datatype</groupId>
+ <artifactId>jackson-datatype-joda</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jsonschema2pojo</groupId>
+ <artifactId>jsonschema2pojo-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.streams</groupId>
+ <artifactId>streams-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.streams</groupId>
+ <artifactId>streams-pojo</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.streams</groupId>
+ <artifactId>streams-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.streams</groupId>
+ <artifactId>streams-util</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.jayway.jsonpath</groupId>
+ <artifactId>json-path</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.jayway.jsonpath</groupId>
+ <artifactId>json-path-assert</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.sachinhandiekar</groupId>
+ <artifactId>jInstagram</artifactId>
+ <version>1.0.7</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <version>1.3</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <sourceDirectory>src/main/java</sourceDirectory>
+ <testSourceDirectory>src/test/java</testSourceDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+ <testResources>
+ <testResource>
+ <directory>src/test/resources</directory>
+ </testResource>
+ </testResources>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>1.8</version>
+ <executions>
+ <execution>
+ <id>add-source</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>target/generated-sources/jsonschema2pojo</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.jsonschema2pojo</groupId>
+ <artifactId>jsonschema2pojo-maven-plugin</artifactId>
+ <configuration>
+ <addCompileSourceRoot>true</addCompileSourceRoot>
+ <generateBuilders>true</generateBuilders>
+ <sourcePaths>
+ <sourcePath>src/main/jsonschema/com/instagram/InstagramConfiguration.json</sourcePath>
+ <sourcePath>src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json</sourcePath>
+ </sourcePaths>
+ <outputDirectory>target/generated-sources/jsonschema2pojo</outputDirectory>
+ <targetPackage>org.apache.streams.instagram.pojo</targetPackage>
+ <useLongIntegers>true</useLongIntegers>
+ <useJodaDates>true</useJodaDates>
+ </configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
new file mode 100644
index 0000000..f771856
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.streams.instagram;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Preconditions;
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigRenderOptions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+import java.io.IOException;
+
+/**
+ * Created by sblackmon on 12/10/13.
+ */
+public class InstagramConfigurator {
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(InstagramConfigurator.class);
+ private final static ObjectMapper mapper = new ObjectMapper();
+
+
+ public static InstagramConfiguration detectInstagramConfiguration(Config config) {
+
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ Validator validator = factory.getValidator();
+
+ InstagramConfiguration instagramConfiguration = null;
+ try {
+ instagramConfiguration = mapper.readValue(config.root().render(ConfigRenderOptions.concise()), InstagramConfiguration.class);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ Preconditions.checkNotNull(instagramConfiguration);
+
+ Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
+
+ return instagramConfiguration;
+ }
+
+ public static InstagramUserInformationConfiguration detectInstagramUserInformationConfiguration(Config config) {
+
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ Validator validator = factory.getValidator();
+
+ InstagramUserInformationConfiguration instagramConfiguration = null;
+ try {
+ instagramConfiguration = mapper.readValue(config.root().render(ConfigRenderOptions.concise()), InstagramUserInformationConfiguration.class);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ Preconditions.checkNotNull(instagramConfiguration);
+
+ Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
+
+ return instagramConfiguration;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
new file mode 100644
index 0000000..14260e3
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.streams.instagram.processor;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Lists;
+import org.apache.streams.core.StreamsDatum;
+import org.apache.streams.core.StreamsProcessor;
+import org.apache.streams.exceptions.ActivitySerializerException;
+import org.apache.streams.instagram.serializer.InstagramJsonActivitySerializer;
+import org.apache.streams.jackson.StreamsJacksonMapper;
+import org.apache.streams.pojo.json.Activity;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * Created by sblackmon on 12/10/13.
+ */
+public class InstagramTypeConverter implements StreamsProcessor {
+
+ public final static String STREAMS_ID = "InstagramTypeConverter";
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(InstagramTypeConverter.class);
+
+ private ObjectMapper mapper;
+
+ private Queue<MediaFeedData> inQueue;
+ private Queue<StreamsDatum> outQueue;
+
+ private Class inClass;
+ private Class outClass;
+
+ private InstagramJsonActivitySerializer instagramJsonActivitySerializer;
+
+ private int count = 0;
+
+ public final static String TERMINATE = new String("TERMINATE");
+
+ public InstagramTypeConverter(Class inClass, Class outClass) {
+ this.inClass = inClass;
+ this.outClass = outClass;
+ }
+
+ public Queue<StreamsDatum> getProcessorOutputQueue() {
+ return outQueue;
+ }
+
+ public void setProcessorInputQueue(Queue<MediaFeedData> inputQueue) {
+ inQueue = inputQueue;
+ }
+
+ public Object convert(ObjectNode event, Class inClass, Class outClass) throws ActivitySerializerException, JsonProcessingException {
+
+ Object result = null;
+
+ if( outClass.equals( Activity.class )) {
+ LOGGER.debug("ACTIVITY");
+ result = instagramJsonActivitySerializer.deserialize(
+ mapper.writeValueAsString(event));
+ } else if( outClass.equals( ObjectNode.class )) {
+ LOGGER.debug("OBJECTNODE");
+ result = mapper.convertValue(event, ObjectNode.class);
+ } else if( outClass.equals( String.class )) {
+ LOGGER.debug("OBJECTNODE");
+ result = mapper.writeValueAsString(event);
+ }
+
+
+ // no supported conversion were applied
+ if( result != null ) {
+ count ++;
+ return result;
+ }
+
+ LOGGER.debug("CONVERT FAILED");
+
+ return null;
+
+ }
+
+ public boolean validate(Object document, Class klass) {
+
+ // TODO
+ return true;
+ }
+
+ public boolean isValidJSON(final String json) {
+ boolean valid = false;
+ try {
+ final JsonParser parser = new ObjectMapper().getJsonFactory()
+ .createJsonParser(json);
+ while (parser.nextToken() != null) {
+ }
+ valid = true;
+ } catch (JsonParseException jpe) {
+ LOGGER.warn("validate: {}", jpe);
+ } catch (IOException ioe) {
+ LOGGER.warn("validate: {}", ioe);
+ }
+
+ return valid;
+ }
+
+ @Override
+ public List<StreamsDatum> process(StreamsDatum entry) {
+
+ StreamsDatum result = null;
+
+ try {
+
+ Object item = entry.getDocument();
+ ObjectNode node;
+
+ LOGGER.debug("{} processing {}", STREAMS_ID, item.getClass());
+
+ if( item instanceof String ) {
+
+ // if the target is string, just pass-through
+ if( String.class.equals(outClass)) {
+ result = entry;
+ }
+ else {
+ // first check for valid json
+ node = (ObjectNode)mapper.readTree((String)item);
+
+ Object out = convert(node, String.class, outClass);
+
+ if( out != null && validate(out, outClass))
+ result = new StreamsDatum(out);
+ }
+
+ } else if( item instanceof ObjectNode ) {
+
+ // first check for valid json
+ node = (ObjectNode)mapper.valueToTree(item);
+
+ Object out = convert(node, ObjectNode.class, outClass);
+
+ if( out != null && validate(out, outClass))
+ result = new StreamsDatum(out);
+
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ if( result != null )
+ return Lists.newArrayList(result);
+ else
+ return Lists.newArrayList();
+ }
+
+ @Override
+ public void prepare(Object o) {
+ mapper = new StreamsJacksonMapper();
+ instagramJsonActivitySerializer = new InstagramJsonActivitySerializer();
+ }
+
+ @Override
+ public void cleanUp() {
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java
new file mode 100644
index 0000000..d3e7179
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java
@@ -0,0 +1,409 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.streams.instagram.provider;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Queues;
+import com.typesafe.config.Config;
+import org.apache.streams.config.StreamsConfigurator;
+import org.apache.streams.core.DatumStatusCounter;
+import org.apache.streams.core.StreamsDatum;
+import org.apache.streams.core.StreamsProvider;
+import org.apache.streams.core.StreamsResultSet;
+import org.apache.streams.instagram.InstagramConfigurator;
+import org.apache.streams.instagram.InstagramUserInformationConfiguration;
+import org.apache.streams.jackson.StreamsJacksonMapper;
+import org.jinstagram.Instagram;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class InstagramTimelineProvider implements StreamsProvider, Serializable {
+
+ public final static String STREAMS_ID = "InstagramTimelineProvider";
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(InstagramTimelineProvider.class);
+ public static final int MAX_NUMBER_WAITING = 10000;
+
+ private static StreamsJacksonMapper mapper = StreamsJacksonMapper.getInstance();
+ private InstagramUserInformationConfiguration config;
+
+ private Class klass;
+ protected final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+ public InstagramUserInformationConfiguration getConfig() {
+ return config;
+ }
+
+ public void setConfig(InstagramUserInformationConfiguration config) {
+ this.config = config;
+ }
+
+ protected Iterator<Long[]> idsBatches;
+ protected Iterator<String[]> screenNameBatches;
+
+ protected volatile Queue<StreamsDatum> providerQueue;
+
+ protected int idsCount;
+ protected Instagram client;
+
+
+ protected ExecutorService executor;
+
+ protected DateTime start;
+ protected DateTime end;
+
+ protected final AtomicBoolean running = new AtomicBoolean();
+
+ private static ExecutorService getExecutor() {
+ return Executors.newSingleThreadExecutor();
+ }
+
+ public InstagramTimelineProvider() {
+ Config config = StreamsConfigurator.config.getConfig("instagram");
+ this.config = InstagramConfigurator.detectInstagramUserInformationConfiguration(config);
+ }
+
+ public InstagramTimelineProvider(InstagramUserInformationConfiguration config) {
+ this.config = config;
+ }
+
+ public InstagramTimelineProvider(Class klass) {
+ Config config = StreamsConfigurator.config.getConfig("instagram");
+ this.config = InstagramConfigurator.detectInstagramUserInformationConfiguration(config);
+ this.klass = klass;
+ }
+
+ public InstagramTimelineProvider(InstagramUserInformationConfiguration config, Class klass) {
+ this.config = config;
+ this.klass = klass;
+ }
+
+
+ public Queue<StreamsDatum> getProviderQueue() {
+ return this.providerQueue;
+ }
+
+ @Override
+ public void startStream() {
+ LOGGER.debug("{} startStream", STREAMS_ID);
+
+ Preconditions.checkArgument(idsBatches.hasNext() || screenNameBatches.hasNext());
+
+ LOGGER.info("readCurrent");
+
+ while(idsBatches.hasNext())
+ loadBatch(idsBatches.next());
+
+ while(screenNameBatches.hasNext())
+ loadBatch(screenNameBatches.next());
+
+ executor.shutdown();
+ }
+
+ private void loadBatch(Long[] ids) {
+
+ // twitter4j implementation below - replace with jInstagram
+
+// Twitter client = getTwitterClient();
+// int keepTrying = 0;
+//
+// // keep trying to load, give it 5 attempts.
+// //while (keepTrying < 10)
+// while (keepTrying < 1)
+// {
+// try
+// {
+// long[] toQuery = new long[ids.length];
+// for(int i = 0; i < ids.length; i++)
+// toQuery[i] = ids[i];
+//
+// for (User tStat : client.lookupUsers(toQuery)) {
+//
+// TwitterTimelineProviderTask providerTask = new TwitterTimelineProviderTask(this, client, tStat.getId());
+// executor.submit(providerTask);
+//
+// }
+// keepTrying = 10;
+// }
+// catch(TwitterException twitterException) {
+// keepTrying += TwitterErrorHandler.handleTwitterError(client, twitterException);
+// }
+// catch(Exception e) {
+// keepTrying += TwitterErrorHandler.handleTwitterError(client, e);
+// }
+// }
+ }
+
+ private void loadBatch(String[] ids) {
+
+ // twitter4j implementation below - replace with jInstagram
+//
+// Twitter client = getTwitterClient();
+// int keepTrying = 0;
+//
+// // keep trying to load, give it 5 attempts.
+// //while (keepTrying < 10)
+// while (keepTrying < 1)
+// {
+// try
+// {
+// for (User tStat : client.lookupUsers(ids)) {
+//
+// TwitterTimelineProviderTask providerTask = new TwitterTimelineProviderTask(this, client, tStat.getId());
+// executor.submit(providerTask);
+//
+// }
+// keepTrying = 10;
+// }
+// catch(TwitterException twitterException) {
+// keepTrying += TwitterErrorHandler.handleTwitterError(client, twitterException);
+// }
+// catch(Exception e) {
+// keepTrying += TwitterErrorHandler.handleTwitterError(client, e);
+// }
+// }
+ }
+
+ public class InstagramTimelineProviderTask implements Runnable {
+
+ // twitter4j implementation below - replace with jInstagram
+
+ private final Logger LOGGER = LoggerFactory.getLogger(InstagramTimelineProvider.class);
+
+ private InstagramTimelineProvider provider;
+ private Instagram client;
+ private Long id;
+
+ public InstagramTimelineProviderTask(InstagramTimelineProvider provider, Instagram client, Long id) {
+ this.provider = provider;
+ this.client = client;
+ this.id = id;
+ }
+
+ @Override
+ public void run() {
+
+ // twitter4j implementation below - replace with jInstagram
+
+// Paging paging = new Paging(1, 200);
+// List<Status> statuses = null;
+// boolean KeepGoing = true;
+// boolean hadFailure = false;
+//
+// do
+// {
+// int keepTrying = 0;
+//
+// // keep trying to load, give it 5 attempts.
+// //This value was chosen because it seemed like a reasonable number of times
+// //to retry capturing a timeline given the sorts of errors that could potentially
+// //occur (network timeout/interruption, faulty client, etc.)
+// while (keepTrying < 5)
+// {
+//
+// try
+// {
+// statuses = client.getUserTimeline(id, paging);
+//
+// for (Status tStat : statuses)
+// {
+// String json = TwitterObjectFactory.getRawJSON(tStat);
+//
+// try {
+// provider.lock.readLock().lock();
+// ComponentUtils.offerUntilSuccess(new StreamsDatum(json), provider.providerQueue);
+// } finally {
+// provider.lock.readLock().unlock();
+// }
+// }
+//
+// paging.setPage(paging.getPage() + 1);
+//
+// keepTrying = 10;
+// }
+// catch(TwitterException twitterException) {
+// keepTrying += TwitterErrorHandler.handleTwitterError(client, twitterException);
+// }
+// catch(Exception e) {
+// keepTrying += TwitterErrorHandler.handleTwitterError(client, e);
+// }
+// }
+// }
+// while (provider.shouldContinuePulling(statuses));
+
+ LOGGER.info(id + " Thread Finished");
+
+ }
+
+ }
+
+ private Map<Long, Long> userPullInfo;
+
+ protected boolean shouldContinuePulling(List<MediaFeedData> statuses) {
+ return (statuses != null) && (statuses.size() > 0);
+ }
+
+ private void sleep()
+ {
+ Thread.yield();
+ try {
+ // wait one tenth of a millisecond
+ Thread.yield();
+ Thread.sleep(1);
+ Thread.yield();
+ }
+ catch(IllegalArgumentException e) {
+ // passing in static values, this will never happen
+ }
+ catch(InterruptedException e) {
+ // noOp, there must have been an issue sleeping
+ }
+ Thread.yield();
+ }
+
+ public StreamsResultSet readCurrent() {
+
+ LOGGER.info("Providing {} docs", providerQueue.size());
+
+ StreamsResultSet result;
+
+ try {
+ lock.writeLock().lock();
+ result = new StreamsResultSet(providerQueue);
+ result.setCounter(new DatumStatusCounter());
+ providerQueue = constructQueue();
+ } finally {
+ lock.writeLock().unlock();
+ }
+
+ if( providerQueue.isEmpty() && executor.isTerminated()) {
+ LOGGER.info("Finished. Cleaning up...");
+
+ running.set(false);
+
+ LOGGER.info("Exiting");
+ }
+
+ return result;
+
+ }
+
+ protected Queue<StreamsDatum> constructQueue() {
+ return Queues.synchronizedQueue(new LinkedBlockingQueue<StreamsDatum>(MAX_NUMBER_WAITING));
+ }
+
+ public StreamsResultSet readNew(BigInteger sequence) {
+ LOGGER.debug("{} readNew", STREAMS_ID);
+ throw new NotImplementedException();
+ }
+
+ public StreamsResultSet readRange(DateTime start, DateTime end) {
+ LOGGER.debug("{} readRange", STREAMS_ID);
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public boolean isRunning() {
+ return running.get();
+ }
+
+ void shutdownAndAwaitTermination(ExecutorService pool) {
+ pool.shutdown(); // Disable new tasks from being submitted
+ try {
+ // Wait a while for existing tasks to terminate
+ if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
+ pool.shutdownNow(); // Cancel currently executing tasks
+ // Wait a while for tasks to respond to being cancelled
+ if (!pool.awaitTermination(10, TimeUnit.SECONDS))
+ System.err.println("Pool did not terminate");
+ }
+ } catch (InterruptedException ie) {
+ // (Re-)Cancel if current thread also interrupted
+ pool.shutdownNow();
+ // Preserve interrupt status
+ Thread.currentThread().interrupt();
+ }
+ }
+
+
+ @Override
+ public void prepare(Object o) {
+
+ executor = getExecutor();
+ running.set(true);
+ try {
+ lock.writeLock().lock();
+ providerQueue = constructQueue();
+ } finally {
+ lock.writeLock().unlock();
+ }
+
+ Preconditions.checkNotNull(providerQueue);
+
+ Preconditions.checkNotNull(this.klass);
+ Preconditions.checkNotNull(config.getAccessToken());
+
+ //idsCount = config.getFollow().size();
+
+ client = getInstagramClient();
+ }
+
+ protected Instagram getInstagramClient()
+ {
+ // twitter4j -> jInstagram
+// String baseUrl = "https://api.instagram.com:443/1.1/";
+//
+// ConfigurationBuilder builder = new ConfigurationBuilder()
+// .setOAuthConsumerKey(config.getOauth().getConsumerKey())
+// .setOAuthConsumerSecret(config.getOauth().getConsumerSecret())
+// .setOAuthAccessToken(config.getOauth().getAccessToken())
+// .setOAuthAccessTokenSecret(config.getOauth().getAccessTokenSecret())
+// .setIncludeEntitiesEnabled(includeEntitiesEnabled)
+// .setJSONStoreEnabled(jsonStoreEnabled)
+// .setAsyncNumThreads(3)
+// .setRestBaseURL(baseUrl)
+// .setIncludeMyRetweetEnabled(Boolean.TRUE)
+// .setPrettyDebugEnabled(Boolean.TRUE);
+//
+// return new InstagramFactory(builder.build()).getInstance();
+ return null;
+ }
+
+ @Override
+ public void cleanUp() {
+ shutdownAndAwaitTermination(executor);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
new file mode 100644
index 0000000..8d92641
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.streams.instagram.serializer;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.streams.data.ActivitySerializer;
+import org.apache.streams.exceptions.ActivitySerializerException;
+import org.apache.streams.pojo.json.Activity;
+
+import java.io.Serializable;
+import java.util.List;
+
+public class InstagramJsonActivitySerializer implements ActivitySerializer<String>, Serializable
+{
+
+ public InstagramJsonActivitySerializer() {
+
+ }
+
+ @Override
+ public String serializationFormat() {
+ return null;
+ }
+
+ @Override
+ public String serialize(Activity deserialized) throws ActivitySerializerException {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public Activity deserialize(String serialized) throws ActivitySerializerException {
+
+ Activity activity = null;
+
+ // implement
+
+ return activity;
+ }
+
+ @Override
+ public List<Activity> deserializeAll(List<String> serializedList) {
+ throw new NotImplementedException();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
new file mode 100644
index 0000000..e71c43e
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.streams.instagram.serializer.util;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import org.apache.streams.exceptions.ActivitySerializerException;
+import org.apache.streams.pojo.json.Activity;
+import org.apache.streams.pojo.json.ActivityObject;
+import org.apache.streams.pojo.json.Actor;
+import org.apache.streams.pojo.json.Provider;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.apache.streams.data.util.ActivityUtil.ensureExtensions;
+
+/**
+ * Provides utilities for working with Activity objects within the context of Instagram
+ */
+public class InstagramActivityUtil {
+
+ /**
+ * Updates the given Activity object with the values from the item
+ * @param item the object to use as the source
+ * @param activity the target of the updates. Will receive all values from the tweet.
+ * @throws ActivitySerializerException
+ */
+ public static void updateActivity(MediaFeedData item, Activity activity) throws ActivitySerializerException {
+
+ }
+
+ /**
+ * Builds the actor
+ * @param item the item
+ * @return a valid Actor
+ */
+ public static Actor buildActor(MediaFeedData item) {
+ Actor actor = new Actor();
+ return actor;
+ }
+
+ /**
+ * Builds the ActivityObject
+ * @param item the item
+ * @return a valid Activity Object
+ */
+ public static ActivityObject buildActivityObject(MediaFeedData item) {
+ ActivityObject actObj = new ActivityObject();
+ return actObj;
+ }
+
+
+ /**
+ * Updates the content, and associated fields, with those from the given tweet
+ * @param activity the target of the updates. Will receive all values from the tweet.
+ * @param item the object to use as the source
+ * @param verb the verb for the given activity's type
+ */
+ public static void updateActivityContent(Activity activity, MediaFeedData item, String verb) {
+
+ }
+
+ /**
+ * Gets the links from the Instagram event
+ * @param item the object to use as the source
+ * @return a list of links corresponding to the expanded URL
+ */
+ public static List<String> getLinks(MediaFeedData item) {
+ List<String> links = Lists.newArrayList();
+ return links;
+ }
+
+ /**
+ * Adds the location extension and populates with teh instagram data
+ * @param activity the Activity object to update
+ * @param item the object to use as the source
+ */
+ public static void addLocationExtension(Activity activity, MediaFeedData item) {
+ Map<String, Object> extensions = ensureExtensions(activity);
+ Map<String, Object> location = new HashMap<String, Object>();
+
+ }
+
+ /**
+ * Gets the common instagram {@link org.apache.streams.pojo.json.Provider} object
+ * @return a provider object representing Instagram
+ */
+ public static Provider getProvider() {
+ Provider provider = new Provider();
+ provider.setId("id:providers:instagram");
+ provider.setDisplayName("Instagram");
+ return provider;
+ }
+ /**
+ * Adds the given Instagram event to the activity as an extension
+ * @param activity the Activity object to update
+ * @param event the Instagram event to add as the extension
+ */
+ public static void addInstagramExtension(Activity activity, ObjectNode event) {
+ Map<String, Object> extensions = org.apache.streams.data.util.ActivityUtil.ensureExtensions(activity);
+ extensions.put("instagram", event);
+ }
+ /**
+ * Formats the ID to conform with the Apache Streams activity ID convention
+ * @param idparts the parts of the ID to join
+ * @return a valid Activity ID in format "id:instagram:part1:part2:...partN"
+ */
+ public static String formatId(String... idparts) {
+ return Joiner.on(":").join(Lists.asList("id:instagram", idparts));
+ }
+
+ /**
+ * Takes various parameters from the instagram object that are currently not part of teh
+ * activity schema and stores them in a generic extensions attribute
+ * @param activity
+ * @param item
+ */
+ public static void addInstagramExtensions(Activity activity, MediaFeedData item) {
+ Map<String, Object> extensions = ensureExtensions(activity);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
new file mode 100644
index 0000000..18a59b9
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
@@ -0,0 +1,20 @@
+{
+ "type": "object",
+ "$schema": "http://json-schema.org/draft-03/schema",
+ "id": "#",
+ "javaType" : "org.apache.streams.instagram.InstagramConfiguration",
+ "javaInterfaces": ["java.io.Serializable"],
+ "properties": {
+ "version": {
+ "type": "string",
+ "description": "The version"
+ },
+ "endpoint": {
+ "type": "string",
+ "description": "The endpoint"
+ },
+ "accessToken": {
+ "type": "string"
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
new file mode 100644
index 0000000..4b75ee4
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
@@ -0,0 +1,17 @@
+{
+ "type": "object",
+ "$schema": "http://json-schema.org/draft-03/schema",
+ "id": "#",
+ "javaType" : "org.apache.streams.instagram.InstagramUserInformationConfiguration",
+ "extends": {"$ref":"InstagramConfiguration.json"},
+ "javaInterfaces": ["java.io.Serializable"],
+ "properties": {
+ "info": {
+ "type": "array",
+ "description": "A list of user IDs, indicating the users whose posts should be delivered on the stream",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/main/resources/reference.conf
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/resources/reference.conf b/streams-contrib/streams-provider-instagram/src/main/resources/reference.conf
new file mode 100644
index 0000000..9a01bf6
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/resources/reference.conf
@@ -0,0 +1,5 @@
+instagram {
+ version = "v1"
+ endpoint = "sample"
+ accessToken = ""
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/537b97aa/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
new file mode 100644
index 0000000..fcf5e81
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.streams.twitter.test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.lang.StringUtils;
+import org.apache.streams.instagram.serializer.InstagramJsonActivitySerializer;
+import org.apache.streams.jackson.StreamsJacksonMapper;
+import org.apache.streams.pojo.json.Activity;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertThat;
+
+/**
+* Created with IntelliJ IDEA.
+* User: sblackmon
+* Date: 8/20/13
+* Time: 5:57 PM
+* To change this template use File | Settings | File Templates.
+*/
+public class InstagramActivitySerDeTest {
+
+ private final static Logger LOGGER = LoggerFactory.getLogger(InstagramActivitySerDeTest.class);
+ private ObjectMapper mapper = StreamsJacksonMapper.getInstance();
+
+ private InstagramJsonActivitySerializer instagramJsonActivitySerializer = new InstagramJsonActivitySerializer();
+
+ // remove @Ignore after implementation
+ @Ignore
+ @Test
+ public void Tests()
+ {
+ InputStream is = InstagramActivitySerDeTest.class.getResourceAsStream("/test.txt");
+ InputStreamReader isr = new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr);
+
+ try {
+ while (br.ready()) {
+ String line = br.readLine();
+ if(!StringUtils.isEmpty(line))
+ {
+ LOGGER.info("raw: {}", line);
+
+ // convert to MediaFeedData?
+ Activity activity = instagramJsonActivitySerializer.deserialize(line);
+
+ String activitystring = mapper.writeValueAsString(activity);
+
+ LOGGER.info("activity: {}", activitystring);
+
+ assertThat(activity, is(not(nullValue())));
+
+ assertThat(activity.getId(), is(not(nullValue())));
+ assertThat(activity.getActor(), is(not(nullValue())));
+ assertThat(activity.getActor().getId(), is(not(nullValue())));
+ assertThat(activity.getVerb(), is(not(nullValue())));
+ assertThat(activity.getProvider(), is(not(nullValue())));
+
+ }
+ }
+ } catch( Exception e ) {
+ System.out.println(e);
+ e.printStackTrace();
+ Assert.fail();
+ }
+ }
+}
[17/18] git commit: Fixed merge issue for rbnks/STREAMS-121
Posted by sb...@apache.org.
Fixed merge issue for rbnks/STREAMS-121
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/2f0a20f3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/2f0a20f3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/2f0a20f3
Branch: refs/heads/master
Commit: 2f0a20f30eb8878b74126190435819a02d714452
Parents: 110dddb
Author: mfranklin <mf...@apache.org>
Authored: Tue Jul 15 12:46:15 2014 -0400
Committer: mfranklin <mf...@apache.org>
Committed: Tue Jul 15 12:46:15 2014 -0400
----------------------------------------------------------------------
streams-contrib/pom.xml | 1 -
1 file changed, 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/2f0a20f3/streams-contrib/pom.xml
----------------------------------------------------------------------
diff --git a/streams-contrib/pom.xml b/streams-contrib/pom.xml
index e225af1..699274e 100644
--- a/streams-contrib/pom.xml
+++ b/streams-contrib/pom.xml
@@ -59,7 +59,6 @@
<module>streams-provider-sysomos</module>
<module>streams-provider-rss</module>
<module>streams-processor-regex</module>
- <module>streams-provider-instagram</module>
</modules>
<dependencyManagement>
[16/18] git commit: STREAMS-121 | Change volatile booleans to
AtomicBooleans per pull request feedbask
Posted by sb...@apache.org.
STREAMS-121 | Change volatile booleans to AtomicBooleans per pull request feedbask
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/110dddbd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/110dddbd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/110dddbd
Branch: refs/heads/master
Commit: 110dddbd25eefbce5ec959103f6004cca5864f28
Parents: 45510b2
Author: rebanks <re...@w2odigital.com>
Authored: Mon Jul 14 14:05:37 2014 -0500
Committer: rebanks <re...@w2odigital.com>
Committed: Mon Jul 14 14:05:37 2014 -0500
----------------------------------------------------------------------
.../apache/streams/instagram/InstagramConfigurator.java | 2 +-
.../instagram/provider/InstagramRecentMediaCollector.java | 9 +++++----
.../instagram/provider/InstagramRecentMediaProvider.java | 10 +++++-----
.../provider/InstagramRecentMediaProviderTest.java | 3 ++-
4 files changed, 13 insertions(+), 11 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/110dddbd/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
index cab072d..11e6d79 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
@@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
- *
+ *
*/
public class InstagramConfigurator {
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/110dddbd/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
index e459a0a..4f27e49 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
@@ -25,6 +25,7 @@ import org.slf4j.LoggerFactory;
import java.util.Queue;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Executes on all of the Instagram requests to collect the media feed data.
@@ -43,14 +44,14 @@ public class InstagramRecentMediaCollector implements Runnable {
protected Queue dataQueue; //exposed for testing
private InstagramUserInformationConfiguration config;
private Instagram instagramClient;
- private volatile boolean isCompleted;
+ private AtomicBoolean isCompleted;
public InstagramRecentMediaCollector(Queue<MediaFeedData> queue, InstagramUserInformationConfiguration config) {
this.dataQueue = queue;
this.config = config;
this.instagramClient = new Instagram(this.config.getClientId());
- this.isCompleted = false;
+ this.isCompleted = new AtomicBoolean(false);
}
/**
@@ -151,7 +152,7 @@ public class InstagramRecentMediaCollector implements Runnable {
* @return true when the collector has queued all of available media feed data for the provided users.
*/
public boolean isCompleted() {
- return this.isCompleted;
+ return this.isCompleted.get();
}
@Override
@@ -159,6 +160,6 @@ public class InstagramRecentMediaCollector implements Runnable {
for(Long userId : getUserIds()) {
getUserMedia(userId);
}
- this.isCompleted = true;
+ this.isCompleted.set(true);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/110dddbd/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
index 3d67a48..30ddda4 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
@@ -29,6 +29,7 @@ import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Instagram {@link org.apache.streams.core.StreamsProvider} that provides the recent media data for a group of users
@@ -39,7 +40,7 @@ public class InstagramRecentMediaProvider implements StreamsProvider {
private InstagramRecentMediaCollector dataCollector;
protected Queue<MediaFeedData> mediaFeedQueue; //exposed for testing
private ExecutorService executorService;
- private volatile boolean isCompleted;
+ private AtomicBoolean isCompleted;
public InstagramRecentMediaProvider() {
this(InstagramConfigurator.detectInstagramUserInformationConfiguration(StreamsConfigurator.config.getConfig("instagram")));
@@ -48,7 +49,6 @@ public class InstagramRecentMediaProvider implements StreamsProvider {
public InstagramRecentMediaProvider(InstagramUserInformationConfiguration config) {
this.config = config;
this.mediaFeedQueue = Queues.newConcurrentLinkedQueue();
- this.isCompleted = false;
}
@Override
@@ -77,7 +77,7 @@ public class InstagramRecentMediaProvider implements StreamsProvider {
batch.add(new StreamsDatum(data, data.getId()));
}
}
- this.isCompleted = batch.size() == 0 && this.mediaFeedQueue.isEmpty() && this.dataCollector.isCompleted();
+ this.isCompleted.set(batch.size() == 0 && this.mediaFeedQueue.isEmpty() && this.dataCollector.isCompleted());
return new StreamsResultSet(batch);
}
@@ -93,12 +93,12 @@ public class InstagramRecentMediaProvider implements StreamsProvider {
@Override
public boolean isRunning() {
- return !this.isCompleted;
+ return !this.isCompleted.get();
}
@Override
public void prepare(Object configurationObject) {
-
+ this.isCompleted = new AtomicBoolean(false);
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/110dddbd/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
index 59e90b4..d81f85a 100644
--- a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
@@ -63,7 +63,7 @@ public class InstagramRecentMediaProviderTest {
return collectorStub;
}
};
-
+ provider.prepare(null);
provider.startStream();
latch.await();
@@ -141,6 +141,7 @@ public class InstagramRecentMediaProviderTest {
};
}
};
+ provider.prepare(null);
provider.startStream();
while(provider.isRunning()) {
try {
[06/18] git commit: STREAMS-122 | Updated the InstagramActivityUtil
class to fully map Instagram MediaFeedData objects to Activities. Updated
tests so that this deserialization and mapping can be tested
Posted by sb...@apache.org.
STREAMS-122 | Updated the InstagramActivityUtil class to fully map Instagram MediaFeedData objects to Activities. Updated tests so that this deserialization and mapping can be tested
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/11636535
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/11636535
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/11636535
Branch: refs/heads/master
Commit: 116365355dad985da52092c85378b5bd7497e907
Parents: 7b301ce
Author: Robert Douglas <rd...@w2odigital.com>
Authored: Tue Jul 1 17:12:57 2014 -0500
Committer: Robert Douglas <rd...@w2odigital.com>
Committed: Tue Jul 1 17:12:57 2014 -0500
----------------------------------------------------------------------
streams-contrib/pom.xml | 1 +
.../InstagramJsonActivitySerializer.java | 22 ++-
.../serializer/util/InstagramActivityUtil.java | 170 ++++++++++++++++---
.../test/InstagramActivitySerDeTest.java | 24 ++-
.../src/test/resources/testMediaFeedObjects.txt | 2 +
5 files changed, 181 insertions(+), 38 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/11636535/streams-contrib/pom.xml
----------------------------------------------------------------------
diff --git a/streams-contrib/pom.xml b/streams-contrib/pom.xml
index 620f68e..699274e 100644
--- a/streams-contrib/pom.xml
+++ b/streams-contrib/pom.xml
@@ -47,6 +47,7 @@
<module>streams-amazon-aws</module>
<!--<module>streams-processor-lucene</module>-->
<!--<module>streams-processor-tika</module>-->
+ <module>streams-provider-instagram</module>
<module>streams-processor-json</module>
<module>streams-processor-urls</module>
<module>streams-provider-datasift</module>
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/11636535/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
index 8d92641..c5bbdf1 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/InstagramJsonActivitySerializer.java
@@ -18,14 +18,21 @@
package org.apache.streams.instagram.serializer;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.NotImplementedException;
import org.apache.streams.data.ActivitySerializer;
import org.apache.streams.exceptions.ActivitySerializerException;
+import org.apache.streams.jackson.StreamsJacksonMapper;
import org.apache.streams.pojo.json.Activity;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import java.io.IOException;
import java.io.Serializable;
import java.util.List;
+import static org.apache.streams.instagram.serializer.util.InstagramActivityUtil.updateActivity;
+
public class InstagramJsonActivitySerializer implements ActivitySerializer<String>, Serializable
{
@@ -46,9 +53,20 @@ public class InstagramJsonActivitySerializer implements ActivitySerializer<Strin
@Override
public Activity deserialize(String serialized) throws ActivitySerializerException {
- Activity activity = null;
+ ObjectMapper mapper = StreamsJacksonMapper.getInstance();
+ MediaFeedData mediaFeedData = null;
+
+ try {
+ mediaFeedData = mapper.readValue(serialized, MediaFeedData.class);
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ Activity activity = new Activity();
- // implement
+ updateActivity(mediaFeedData, activity);
return activity;
}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/11636535/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
index e71c43e..0561ba7 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
@@ -19,16 +19,21 @@
package org.apache.streams.instagram.serializer.util;
-import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import org.apache.streams.exceptions.ActivitySerializerException;
-import org.apache.streams.pojo.json.Activity;
-import org.apache.streams.pojo.json.ActivityObject;
-import org.apache.streams.pojo.json.Actor;
-import org.apache.streams.pojo.json.Provider;
+import org.apache.streams.pojo.json.*;
+import org.jinstagram.entity.common.ImageData;
+import org.jinstagram.entity.common.Images;
+import org.jinstagram.entity.common.VideoData;
+import org.jinstagram.entity.common.Videos;
import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -39,7 +44,7 @@ import static org.apache.streams.data.util.ActivityUtil.ensureExtensions;
* Provides utilities for working with Activity objects within the context of Instagram
*/
public class InstagramActivityUtil {
-
+ private static final Logger LOGGER = LoggerFactory.getLogger(InstagramActivityUtil.class);
/**
* Updates the given Activity object with the values from the item
* @param item the object to use as the source
@@ -47,7 +52,22 @@ public class InstagramActivityUtil {
* @throws ActivitySerializerException
*/
public static void updateActivity(MediaFeedData item, Activity activity) throws ActivitySerializerException {
+ activity.setActor(buildActor(item));
+ activity.setPublished(new DateTime(Long.parseLong(item.getCreatedTime()) * 1000));
+
+ activity.setId(formatId(activity.getVerb(),
+ Optional.fromNullable(
+ item.getId())
+ .orNull()));
+
+ activity.setProvider(getProvider());
+ activity.setUrl(item.getLink());
+ activity.setObject(buildActivityObject(item));
+ if(item.getCaption() != null)
+ activity.setContent(item.getCaption().getText());
+
+ addInstagramExtensions(activity, item);
}
/**
@@ -57,6 +77,18 @@ public class InstagramActivityUtil {
*/
public static Actor buildActor(MediaFeedData item) {
Actor actor = new Actor();
+
+ Image image = new Image();
+ image.setUrl(item.getUser().getProfilePictureUrl());
+
+ Map<String, Object> extensions = new HashMap<String, Object>();
+ extensions.put("screenName", item.getUser().getUserName());
+
+ actor.setId(formatId(String.valueOf(item.getUser().getId())));
+ actor.setImage(image);
+ actor.setAdditionalProperty("extensions", extensions);
+ actor.setAdditionalProperty("handle", item.getUser().getUserName());
+
return actor;
}
@@ -67,18 +99,90 @@ public class InstagramActivityUtil {
*/
public static ActivityObject buildActivityObject(MediaFeedData item) {
ActivityObject actObj = new ActivityObject();
+
+ actObj.setObjectType(item.getType());
+ actObj.setAttachments(buildActivityObjectAttachments(item));
+
return actObj;
}
+ /**
+ * Builds all of the attachments associated with a MediaFeedData object
+ *
+ * @param item
+ * @return
+ */
+ public static List<ActivityObject> buildActivityObjectAttachments(MediaFeedData item) {
+ List<ActivityObject> attachments = new ArrayList<ActivityObject>();
+
+ addImageObjects(attachments, item);
+ addVideoObjects(attachments, item);
+
+ return attachments;
+ }
/**
- * Updates the content, and associated fields, with those from the given tweet
- * @param activity the target of the updates. Will receive all values from the tweet.
- * @param item the object to use as the source
- * @param verb the verb for the given activity's type
+ * Adds any image objects to the attachment field
+ * @param attachments
+ * @param item
+ */
+ public static void addImageObjects(List<ActivityObject> attachments, MediaFeedData item) {
+ Images images = item.getImages();
+
+ if(images != null) {
+ try {
+ ImageData thumbnail = images.getThumbnail();
+ ImageData lowResolution = images.getLowResolution();
+
+ ActivityObject thumbnailObject = new ActivityObject();
+ Image thumbnailImage = new Image();
+ thumbnailImage.setUrl(thumbnail.getImageUrl());
+ thumbnailImage.setHeight((double) thumbnail.getImageHeight());
+ thumbnailImage.setWidth((double) thumbnail.getImageWidth());
+ thumbnailObject.setImage(thumbnailImage);
+ thumbnailObject.setObjectType("image");
+
+ ActivityObject lowResolutionObject = new ActivityObject();
+ Image lowResolutionImage = new Image();
+ lowResolutionImage.setUrl(lowResolution.getImageUrl());
+ lowResolutionImage.setHeight((double) lowResolution.getImageHeight());
+ lowResolutionImage.setWidth((double) lowResolution.getImageWidth());
+ lowResolutionObject.setImage(lowResolutionImage);
+ lowResolutionObject.setObjectType("image");
+
+ attachments.add(thumbnailObject);
+ attachments.add(lowResolutionObject);
+ } catch (Exception e) {
+ LOGGER.error("Failed to add image objects: {}", e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Adds any video objects to the attachment field
+ * @param attachments
+ * @param item
*/
- public static void updateActivityContent(Activity activity, MediaFeedData item, String verb) {
+ public static void addVideoObjects(List<ActivityObject> attachments, MediaFeedData item) {
+ Videos videos = item.getVideos();
+
+ if(videos != null) {
+ try {
+ VideoData lowResolutionVideo = videos.getLowResolution();
+
+ ActivityObject lowResolutionVideoObject = new ActivityObject();
+ Image lowResolutionVideoImage = new Image();
+ lowResolutionVideoImage.setUrl(lowResolutionVideo.getUrl());
+ lowResolutionVideoImage.setHeight((double) lowResolutionVideo.getHeight());
+ lowResolutionVideoImage.setWidth((double) lowResolutionVideo.getWidth());
+ lowResolutionVideoObject.setImage(lowResolutionVideoImage);
+ lowResolutionVideoObject.setObjectType("video");
+ attachments.add(lowResolutionVideoObject);
+ } catch (Exception e) {
+ LOGGER.error("Failed to add video objects: {}", e.getMessage());
+ }
+ }
}
/**
@@ -98,7 +202,14 @@ public class InstagramActivityUtil {
*/
public static void addLocationExtension(Activity activity, MediaFeedData item) {
Map<String, Object> extensions = ensureExtensions(activity);
- Map<String, Object> location = new HashMap<String, Object>();
+
+ if(item.getLocation() != null) {
+ Map<String, Object> coordinates = new HashMap<String, Object>();
+ coordinates.put("type", "Point");
+ coordinates.put("coordinates", "[" + item.getLocation().getLatitude() + "," + item.getLocation().getLongitude() + "]");
+
+ extensions.put("coordinates", coordinates);
+ }
}
@@ -112,15 +223,7 @@ public class InstagramActivityUtil {
provider.setDisplayName("Instagram");
return provider;
}
- /**
- * Adds the given Instagram event to the activity as an extension
- * @param activity the Activity object to update
- * @param event the Instagram event to add as the extension
- */
- public static void addInstagramExtension(Activity activity, ObjectNode event) {
- Map<String, Object> extensions = org.apache.streams.data.util.ActivityUtil.ensureExtensions(activity);
- extensions.put("instagram", event);
- }
+
/**
* Formats the ID to conform with the Apache Streams activity ID convention
* @param idparts the parts of the ID to join
@@ -138,5 +241,28 @@ public class InstagramActivityUtil {
*/
public static void addInstagramExtensions(Activity activity, MediaFeedData item) {
Map<String, Object> extensions = ensureExtensions(activity);
+
+ addLocationExtension(activity, item);
+
+ Map<String, Object> likes = new HashMap<String, Object>();
+ likes.put("count", item.getLikes().getCount());
+ extensions.put("likes", likes);
+
+ extensions.put("hashtags", item.getTags());
+
+ Image standardResolution = new Image();
+ if(item.getType() == "image" && item.getImages() != null) {
+ ImageData standardResolutionData = item.getImages().getStandardResolution();
+ standardResolution.setHeight((double)standardResolutionData.getImageHeight());
+ standardResolution.setWidth((double)standardResolutionData.getImageWidth());
+ standardResolution.setUrl(standardResolutionData.getImageUrl());
+ } else if(item.getType() == "video" && item.getVideos() != null) {
+ VideoData standardResolutionData = item.getVideos().getStandardResolution();
+ standardResolution.setHeight((double)standardResolutionData.getHeight());
+ standardResolution.setWidth((double)standardResolutionData.getWidth());
+ standardResolution.setUrl(standardResolutionData.getUrl());
+ }
+
+ extensions.put("image", standardResolution);
}
-}
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/11636535/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
index fcf5e81..075da80 100644
--- a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
@@ -20,11 +20,12 @@ package org.apache.streams.twitter.test;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
+import org.apache.streams.instagram.serializer.util.InstagramDeserializer;
import org.apache.streams.instagram.serializer.InstagramJsonActivitySerializer;
import org.apache.streams.jackson.StreamsJacksonMapper;
import org.apache.streams.pojo.json.Activity;
+import org.jinstagram.entity.users.feed.MediaFeedData;
import org.junit.Assert;
-import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,6 +34,7 @@ import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
+import static org.apache.streams.instagram.serializer.util.InstagramActivityUtil.updateActivity;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;
@@ -46,16 +48,11 @@ import static org.junit.Assert.assertThat;
public class InstagramActivitySerDeTest {
private final static Logger LOGGER = LoggerFactory.getLogger(InstagramActivitySerDeTest.class);
- private ObjectMapper mapper = StreamsJacksonMapper.getInstance();
- private InstagramJsonActivitySerializer instagramJsonActivitySerializer = new InstagramJsonActivitySerializer();
-
- // remove @Ignore after implementation
- @Ignore
@Test
- public void Tests()
- {
- InputStream is = InstagramActivitySerDeTest.class.getResourceAsStream("/test.txt");
+ public void Tests() {
+ InstagramDeserializer instagramDeserializer = new InstagramDeserializer("");
+ InputStream is = InstagramActivitySerDeTest.class.getResourceAsStream("/testMediaFeedObjects.txt");
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
@@ -66,13 +63,13 @@ public class InstagramActivitySerDeTest {
{
LOGGER.info("raw: {}", line);
- // convert to MediaFeedData?
- Activity activity = instagramJsonActivitySerializer.deserialize(line);
+ MediaFeedData mediaFeedData = instagramDeserializer.createObjectFromResponse(MediaFeedData.class, line);
- String activitystring = mapper.writeValueAsString(activity);
+ Activity activity = new Activity();
- LOGGER.info("activity: {}", activitystring);
+ LOGGER.info("activity: {}", activity.toString());
+ updateActivity(mediaFeedData, activity);
assertThat(activity, is(not(nullValue())));
assertThat(activity.getId(), is(not(nullValue())));
@@ -80,7 +77,6 @@ public class InstagramActivitySerDeTest {
assertThat(activity.getActor().getId(), is(not(nullValue())));
assertThat(activity.getVerb(), is(not(nullValue())));
assertThat(activity.getProvider(), is(not(nullValue())));
-
}
}
} catch( Exception e ) {
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/11636535/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt b/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
index e69de29..b62e599 100644
--- a/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
+++ b/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
@@ -0,0 +1,2 @@
+{ "attribution":null, "tags":[ ], "type":"image", "location":null, "comments":{ "count":0, "data":[ ] }, "filter":"X-Pro II", "created_time":"1404162054", "link":"http://instagram.com/p/p4ez__syJi/", "likes":{ "count":0, "data":[ ] }, "images":{ "low_resolution":{ "url":"http://scontent-a.cdninstagram.com/hphotos-xpa1/t51.2885-15/10518155_283164271863608_1534480525_a.jpg", "width":306, "height":306 }, "thumbnail":{ "url":"http://scontent-a.cdninstagram.com/hphotos-xpa1/t51.2885-15/10518155_283164271863608_1534480525_s.jpg", "width":150, "height":150 }, "standard_resolution":{ "url":"http://scontent-a.cdninstagram.com/hphotos-xpa1/t51.2885-15/10518155_283164271863608_1534480525_n.jpg", "width":640, "height":640 } }, "users_in_photo":[ ], "caption":{ "created_time":"1404162054", "text":"Testing streams", "from":{ "username":"ktsafford", "profile_picture":"http://images.ak.instagram.com/profiles/anonymousUser.jpg", "id":"1412068271", "full_name":"ktsafford" }, "id":"754488452958068751"
}, "user_has_liked":false, "id":"754488452387644002_1412068271", "user":{ "username":"ktsafford", "website":"", "profile_picture":"http://images.ak.instagram.com/profiles/anonymousUser.jpg", "full_name":"ktsafford", "bio":"", "id":"1412068271" } }
+{ "type":"image", "users_in_photo":[ { "user":{ "username":"kevin", "full_name":"Kevin S", "id":"3", "profile_picture":"..." }, "position":{ "x":0.315, "y":0.9111 } } ], "filter":"Walden", "tags":[ ], "comments":{ "data":[ { "created_time":"1279332030", "text":"Love the sign here", "from":{ "username":"mikeyk", "full_name":"Mikey Krieger", "id":"4", "profile_picture":"http://distillery.s3.amazonaws.com/profiles/profile_1242695_75sq_1293915800.jpg" }, "id":"8" }, { "created_time":"1279341004", "text":"Chilako taco", "from":{ "username":"kevin", "full_name":"Kevin S", "id":"3", "profile_picture":"..." }, "id":"3" } ], "count":2 }, "caption":null, "likes":{ "count":1, "data":[ { "username":"mikeyk", "full_name":"Mikeyk", "id":"4", "profile_picture":"..." } ] }, "link":"http://instagr.am/p/D/", "user":{ "username":"kevin", "full_name":"Kevin S", "profile_picture":"...", "bio":"...", "website":"...", "id":"3" }, "created_time":"1279340983", "images":{ "low_resolution":{ "url":"http://dis
tillery.s3.amazonaws.com/media/2010/07/16/4de37e03aa4b4372843a7eb33fa41cad_6.jpg", "width":306, "height":306 }, "thumbnail":{ "url":"http://distillery.s3.amazonaws.com/media/2010/07/16/4de37e03aa4b4372843a7eb33fa41cad_5.jpg", "width":150, "height":150 }, "standard_resolution":{ "url":"http://distillery.s3.amazonaws.com/media/2010/07/16/4de37e03aa4b4372843a7eb33fa41cad_7.jpg", "width":612, "height":612 } }, "id":"3", "location":null }
[18/18] git commit: Merge branch 'instagram'
Posted by sb...@apache.org.
Merge branch 'instagram'
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/7c5e2900
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/7c5e2900
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/7c5e2900
Branch: refs/heads/master
Commit: 7c5e29007b27f59cdf7120c26a00b59da3ce931e
Parents: b7e4c34 2f0a20f
Author: Steve Blackmon <sb...@w2odigital.com>
Authored: Sun Jul 20 23:46:56 2014 -0500
Committer: Steve Blackmon <sb...@w2odigital.com>
Committed: Sun Jul 20 23:46:56 2014 -0500
----------------------------------------------------------------------
streams-contrib/pom.xml | 1 +
.../streams-provider-instagram/README.md | 17 +
.../InstagramMediaDataActivitySerializer.mup | 894 +++++++++++++++++++
.../metadata/instagram_to_activity_mapping.png | Bin 0 -> 1123684 bytes
.../streams-provider-instagram/pom.xml | 145 +++
.../instagram/InstagramConfigurator.java | 65 ++
.../processor/InstagramTypeConverter.java | 102 +++
.../provider/InstagramRecentMediaCollector.java | 165 ++++
.../provider/InstagramRecentMediaProvider.java | 115 +++
.../InstagramJsonActivitySerializer.java | 78 ++
.../serializer/util/InstagramActivityUtil.java | 285 ++++++
.../serializer/util/InstagramDeserializer.java | 33 +
.../com/instagram/InstagramConfiguration.json | 21 +
.../InstagramUserInformationConfiguration.json | 17 +
.../src/main/resources/reference.conf | 5 +
.../InstagramRecentMediaCollectorTest.java | 175 ++++
.../InstagramRecentMediaProviderTest.java | 160 ++++
.../test/InstagramActivitySerDeTest.java | 88 ++
.../src/test/resources/testMediaFeedObjects.txt | 2 +
.../src/test/resources/testtweets.txt | 695 ++++++++++++++
20 files changed, 3063 insertions(+)
----------------------------------------------------------------------
[12/18] git commit: STREAMS 121 | Added instagram provider and tests
Posted by sb...@apache.org.
STREAMS 121 | Added instagram provider and tests
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/77603934
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/77603934
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/77603934
Branch: refs/heads/master
Commit: 77603934e41ac78ba68f73b3f80ea6852f0acb46
Parents: 815ce2a
Author: rebanks <re...@w2odigital.com>
Authored: Fri Jul 11 16:09:59 2014 -0500
Committer: rebanks <re...@w2odigital.com>
Committed: Fri Jul 11 16:09:59 2014 -0500
----------------------------------------------------------------------
streams-contrib/pom.xml | 1 +
.../streams-provider-instagram/pom.xml | 6 +
.../instagram/InstagramConfigurator.java | 15 +-
.../provider/InstagramRecentMediaCollector.java | 120 ++++++
.../provider/InstagramRecentMediaProvider.java | 97 +++++
.../provider/InstagramTimelineProvider.java | 409 -------------------
.../com/instagram/InstagramConfiguration.json | 13 +-
.../InstagramUserInformationConfiguration.json | 2 +-
.../InstagramRecentMediaCollectorTest.java | 161 ++++++++
.../InstagramRecentMediaProviderTest.java | 145 +++++++
.../test/InstagramActivitySerDeTest.java | 92 -----
11 files changed, 544 insertions(+), 517 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/pom.xml
----------------------------------------------------------------------
diff --git a/streams-contrib/pom.xml b/streams-contrib/pom.xml
index 620f68e..b06c276 100644
--- a/streams-contrib/pom.xml
+++ b/streams-contrib/pom.xml
@@ -58,6 +58,7 @@
<module>streams-provider-sysomos</module>
<module>streams-provider-rss</module>
<module>streams-processor-regex</module>
+ <module>streams-provider-instagram</module>
</modules>
<dependencyManagement>
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/pom.xml
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/pom.xml b/streams-contrib/streams-provider-instagram/pom.xml
index e356a7a..a96ff1c 100644
--- a/streams-contrib/streams-provider-instagram/pom.xml
+++ b/streams-contrib/streams-provider-instagram/pom.xml
@@ -76,6 +76,12 @@
<version>1.3</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
index f771856..4d01605 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
@@ -25,9 +25,6 @@ import com.typesafe.config.ConfigRenderOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.validation.Validation;
-import javax.validation.Validator;
-import javax.validation.ValidatorFactory;
import java.io.IOException;
/**
@@ -41,8 +38,8 @@ public class InstagramConfigurator {
public static InstagramConfiguration detectInstagramConfiguration(Config config) {
- ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
- Validator validator = factory.getValidator();
+// ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+// Validator validator = factory.getValidator();
InstagramConfiguration instagramConfiguration = null;
try {
@@ -52,15 +49,15 @@ public class InstagramConfigurator {
}
Preconditions.checkNotNull(instagramConfiguration);
- Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
+// Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
return instagramConfiguration;
}
public static InstagramUserInformationConfiguration detectInstagramUserInformationConfiguration(Config config) {
- ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
- Validator validator = factory.getValidator();
+// ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+// Validator validator = factory.getValidator();
InstagramUserInformationConfiguration instagramConfiguration = null;
try {
@@ -70,7 +67,7 @@ public class InstagramConfigurator {
}
Preconditions.checkNotNull(instagramConfiguration);
- Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
+// Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
return instagramConfiguration;
}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
new file mode 100644
index 0000000..7eb3fcd
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
@@ -0,0 +1,120 @@
+package org.apache.streams.instagram.provider;
+
+import com.google.common.collect.Sets;
+import org.apache.streams.instagram.InstagramUserInformationConfiguration;
+import org.jinstagram.Instagram;
+import org.jinstagram.entity.users.feed.MediaFeed;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.jinstagram.exceptions.InstagramException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Queue;
+import java.util.Set;
+
+/**
+ *
+ */
+public class InstagramRecentMediaCollector implements Runnable {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(InstagramRecentMediaCollector.class);
+ protected static final int MAX_ATTEMPTS = 5;
+ protected static final int SLEEP_SECS = 5; //5 seconds
+
+ protected Queue dataQueue; //exposed for testing
+ private InstagramUserInformationConfiguration config;
+ private Instagram instagramClient;
+ private volatile boolean isCompleted;
+
+
+ public InstagramRecentMediaCollector(Queue<MediaFeedData> queue, InstagramUserInformationConfiguration config) {
+ this.dataQueue = queue;
+ this.config = config;
+ this.instagramClient = new Instagram(this.config.getClientId());
+ this.isCompleted = false;
+ }
+
+ protected void setInstagramClient(Instagram instagramClient) {
+ this.instagramClient = instagramClient;
+ }
+
+ protected Set<Long> getUserIds() {
+ Set<Long> userIds = Sets.newHashSet();
+ for(String id : config.getUserIds()) {
+ try {
+ userIds.add(Long.parseLong(id));
+ } catch (NumberFormatException nfe) {
+ LOGGER.error("Failed to parse user id, {}, to a long : {}", id, nfe.getMessage());
+ }
+ }
+ return userIds;
+ }
+
+ protected void handleInstagramException(InstagramException instaExec, int attempt) throws InstagramException {
+ LOGGER.debug("RemainingApiLimitStatus: {}", instaExec.getRemainingLimitStatus());
+ if(instaExec.getRemainingLimitStatus() == 0) { //rate limit exception
+ long sleepTime = Math.round(Math.pow(SLEEP_SECS, attempt)) * 1000;
+ try {
+ LOGGER.debug("Encountered rate limit exception, sleeping for {} ms", sleepTime);
+ Thread.sleep(sleepTime);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ }
+ } else {
+ LOGGER.error("Instagram returned an excetpion to the user media request : {}", instaExec.getMessage());
+ throw instaExec;
+ }
+ }
+
+ private void getUserMedia(Long userId) {
+ MediaFeed feed = null;
+ int attempts = 0;
+ int count = 0;
+ do {
+ ++attempts;
+ try {
+ feed = this.instagramClient.getRecentMediaFeed(userId);
+ queueData(feed, userId);
+ count += feed.getData().size();
+ while(feed != null && feed.getPagination() != null && feed.getPagination().hasNextPage()) {
+ feed = this.instagramClient.getRecentMediaNextPage(feed.getPagination());
+ queueData(feed, userId);
+ count += feed.getData().size();
+ }
+ } catch (InstagramException ie) {
+ try {
+ handleInstagramException(ie, attempts);
+ } catch (InstagramException ie2) { //not a rate limit exception, ignore user
+ attempts = MAX_ATTEMPTS;
+ }
+ }
+ } while(feed == null && attempts < MAX_ATTEMPTS);
+ LOGGER.debug("For user, {}, received {} MediaFeedData", userId, count);
+ }
+
+ private void queueData(MediaFeed userFeed, Long userId) {
+ if(userFeed == null) {
+ LOGGER.error("User id, {}, returned a NULL media feed from instagram.", userId);
+ } else {
+ for(MediaFeedData data : userFeed.getData()) {
+ synchronized (this.dataQueue) {
+ while(!this.dataQueue.offer(data)) {
+ Thread.yield();
+ }
+ }
+ }
+ }
+ }
+
+ public boolean isCompleted() {
+ return this.isCompleted;
+ }
+
+ @Override
+ public void run() {
+ for(Long userId : getUserIds()) {
+ getUserMedia(userId);
+ }
+ this.isCompleted = true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
new file mode 100644
index 0000000..3354e54
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
@@ -0,0 +1,97 @@
+package org.apache.streams.instagram.provider;
+
+import com.google.common.collect.Queues;
+import org.apache.streams.config.StreamsConfigurator;
+import org.apache.streams.core.StreamsDatum;
+import org.apache.streams.core.StreamsProvider;
+import org.apache.streams.core.StreamsResultSet;
+import org.apache.streams.instagram.InstagramConfigurator;
+import org.apache.streams.instagram.InstagramUserInformationConfiguration;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.joda.time.DateTime;
+
+import java.math.BigInteger;
+import java.util.Queue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Created by rebanks on 7/9/14.
+ */
+public class InstagramRecentMediaProvider implements StreamsProvider {
+
+ private InstagramUserInformationConfiguration config;
+ private InstagramRecentMediaCollector dataCollector;
+ protected Queue<MediaFeedData> mediaFeedQueue; //exposed for testing
+ private ExecutorService executorService;
+ private volatile boolean isCompleted;
+
+ public InstagramRecentMediaProvider() {
+ this(InstagramConfigurator.detectInstagramUserInformationConfiguration(StreamsConfigurator.config.getConfig("instagram")));
+ }
+
+ public InstagramRecentMediaProvider(InstagramUserInformationConfiguration config) {
+ this.config = config;
+ this.mediaFeedQueue = Queues.newConcurrentLinkedQueue();
+ this.isCompleted = false;
+ }
+
+ @Override
+ public void startStream() {
+ this.dataCollector = getInstagramRecentMediaCollector();
+ this.executorService = Executors.newSingleThreadExecutor();
+ this.executorService.submit(this.dataCollector);
+ }
+
+ protected InstagramRecentMediaCollector getInstagramRecentMediaCollector() {
+ return new InstagramRecentMediaCollector(this.mediaFeedQueue, this.config);
+ }
+
+
+ @Override
+ public StreamsResultSet readCurrent() {
+ Queue<StreamsDatum> batch = Queues.newConcurrentLinkedQueue();
+ MediaFeedData data = null;
+ synchronized (this.mediaFeedQueue) {
+ while(!this.mediaFeedQueue.isEmpty()) {
+ data = this.mediaFeedQueue.poll();
+ batch.add(new StreamsDatum(data, data.getId()));
+ }
+ }
+ this.isCompleted = batch.size() == 0 && this.mediaFeedQueue.isEmpty() && this.dataCollector.isCompleted();
+ return new StreamsResultSet(batch);
+ }
+
+ @Override
+ public StreamsResultSet readNew(BigInteger sequence) {
+ return null;
+ }
+
+ @Override
+ public StreamsResultSet readRange(DateTime start, DateTime end) {
+ return null;
+ }
+
+ @Override
+ public boolean isRunning() {
+ return !this.isCompleted;
+ }
+
+ @Override
+ public void prepare(Object configurationObject) {
+
+ }
+
+ @Override
+ public void cleanUp() {
+ this.executorService.shutdown();
+ try {
+ this.executorService.awaitTermination(5, TimeUnit.SECONDS);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ } finally {
+ this.executorService = null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java
deleted file mode 100644
index d3e7179..0000000
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramTimelineProvider.java
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.streams.instagram.provider;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Queues;
-import com.typesafe.config.Config;
-import org.apache.streams.config.StreamsConfigurator;
-import org.apache.streams.core.DatumStatusCounter;
-import org.apache.streams.core.StreamsDatum;
-import org.apache.streams.core.StreamsProvider;
-import org.apache.streams.core.StreamsResultSet;
-import org.apache.streams.instagram.InstagramConfigurator;
-import org.apache.streams.instagram.InstagramUserInformationConfiguration;
-import org.apache.streams.jackson.StreamsJacksonMapper;
-import org.jinstagram.Instagram;
-import org.jinstagram.entity.users.feed.MediaFeedData;
-import org.joda.time.DateTime;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import sun.reflect.generics.reflectiveObjects.NotImplementedException;
-
-import java.io.Serializable;
-import java.math.BigInteger;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-public class InstagramTimelineProvider implements StreamsProvider, Serializable {
-
- public final static String STREAMS_ID = "InstagramTimelineProvider";
-
- private final static Logger LOGGER = LoggerFactory.getLogger(InstagramTimelineProvider.class);
- public static final int MAX_NUMBER_WAITING = 10000;
-
- private static StreamsJacksonMapper mapper = StreamsJacksonMapper.getInstance();
- private InstagramUserInformationConfiguration config;
-
- private Class klass;
- protected final ReadWriteLock lock = new ReentrantReadWriteLock();
-
- public InstagramUserInformationConfiguration getConfig() {
- return config;
- }
-
- public void setConfig(InstagramUserInformationConfiguration config) {
- this.config = config;
- }
-
- protected Iterator<Long[]> idsBatches;
- protected Iterator<String[]> screenNameBatches;
-
- protected volatile Queue<StreamsDatum> providerQueue;
-
- protected int idsCount;
- protected Instagram client;
-
-
- protected ExecutorService executor;
-
- protected DateTime start;
- protected DateTime end;
-
- protected final AtomicBoolean running = new AtomicBoolean();
-
- private static ExecutorService getExecutor() {
- return Executors.newSingleThreadExecutor();
- }
-
- public InstagramTimelineProvider() {
- Config config = StreamsConfigurator.config.getConfig("instagram");
- this.config = InstagramConfigurator.detectInstagramUserInformationConfiguration(config);
- }
-
- public InstagramTimelineProvider(InstagramUserInformationConfiguration config) {
- this.config = config;
- }
-
- public InstagramTimelineProvider(Class klass) {
- Config config = StreamsConfigurator.config.getConfig("instagram");
- this.config = InstagramConfigurator.detectInstagramUserInformationConfiguration(config);
- this.klass = klass;
- }
-
- public InstagramTimelineProvider(InstagramUserInformationConfiguration config, Class klass) {
- this.config = config;
- this.klass = klass;
- }
-
-
- public Queue<StreamsDatum> getProviderQueue() {
- return this.providerQueue;
- }
-
- @Override
- public void startStream() {
- LOGGER.debug("{} startStream", STREAMS_ID);
-
- Preconditions.checkArgument(idsBatches.hasNext() || screenNameBatches.hasNext());
-
- LOGGER.info("readCurrent");
-
- while(idsBatches.hasNext())
- loadBatch(idsBatches.next());
-
- while(screenNameBatches.hasNext())
- loadBatch(screenNameBatches.next());
-
- executor.shutdown();
- }
-
- private void loadBatch(Long[] ids) {
-
- // twitter4j implementation below - replace with jInstagram
-
-// Twitter client = getTwitterClient();
-// int keepTrying = 0;
-//
-// // keep trying to load, give it 5 attempts.
-// //while (keepTrying < 10)
-// while (keepTrying < 1)
-// {
-// try
-// {
-// long[] toQuery = new long[ids.length];
-// for(int i = 0; i < ids.length; i++)
-// toQuery[i] = ids[i];
-//
-// for (User tStat : client.lookupUsers(toQuery)) {
-//
-// TwitterTimelineProviderTask providerTask = new TwitterTimelineProviderTask(this, client, tStat.getId());
-// executor.submit(providerTask);
-//
-// }
-// keepTrying = 10;
-// }
-// catch(TwitterException twitterException) {
-// keepTrying += TwitterErrorHandler.handleTwitterError(client, twitterException);
-// }
-// catch(Exception e) {
-// keepTrying += TwitterErrorHandler.handleTwitterError(client, e);
-// }
-// }
- }
-
- private void loadBatch(String[] ids) {
-
- // twitter4j implementation below - replace with jInstagram
-//
-// Twitter client = getTwitterClient();
-// int keepTrying = 0;
-//
-// // keep trying to load, give it 5 attempts.
-// //while (keepTrying < 10)
-// while (keepTrying < 1)
-// {
-// try
-// {
-// for (User tStat : client.lookupUsers(ids)) {
-//
-// TwitterTimelineProviderTask providerTask = new TwitterTimelineProviderTask(this, client, tStat.getId());
-// executor.submit(providerTask);
-//
-// }
-// keepTrying = 10;
-// }
-// catch(TwitterException twitterException) {
-// keepTrying += TwitterErrorHandler.handleTwitterError(client, twitterException);
-// }
-// catch(Exception e) {
-// keepTrying += TwitterErrorHandler.handleTwitterError(client, e);
-// }
-// }
- }
-
- public class InstagramTimelineProviderTask implements Runnable {
-
- // twitter4j implementation below - replace with jInstagram
-
- private final Logger LOGGER = LoggerFactory.getLogger(InstagramTimelineProvider.class);
-
- private InstagramTimelineProvider provider;
- private Instagram client;
- private Long id;
-
- public InstagramTimelineProviderTask(InstagramTimelineProvider provider, Instagram client, Long id) {
- this.provider = provider;
- this.client = client;
- this.id = id;
- }
-
- @Override
- public void run() {
-
- // twitter4j implementation below - replace with jInstagram
-
-// Paging paging = new Paging(1, 200);
-// List<Status> statuses = null;
-// boolean KeepGoing = true;
-// boolean hadFailure = false;
-//
-// do
-// {
-// int keepTrying = 0;
-//
-// // keep trying to load, give it 5 attempts.
-// //This value was chosen because it seemed like a reasonable number of times
-// //to retry capturing a timeline given the sorts of errors that could potentially
-// //occur (network timeout/interruption, faulty client, etc.)
-// while (keepTrying < 5)
-// {
-//
-// try
-// {
-// statuses = client.getUserTimeline(id, paging);
-//
-// for (Status tStat : statuses)
-// {
-// String json = TwitterObjectFactory.getRawJSON(tStat);
-//
-// try {
-// provider.lock.readLock().lock();
-// ComponentUtils.offerUntilSuccess(new StreamsDatum(json), provider.providerQueue);
-// } finally {
-// provider.lock.readLock().unlock();
-// }
-// }
-//
-// paging.setPage(paging.getPage() + 1);
-//
-// keepTrying = 10;
-// }
-// catch(TwitterException twitterException) {
-// keepTrying += TwitterErrorHandler.handleTwitterError(client, twitterException);
-// }
-// catch(Exception e) {
-// keepTrying += TwitterErrorHandler.handleTwitterError(client, e);
-// }
-// }
-// }
-// while (provider.shouldContinuePulling(statuses));
-
- LOGGER.info(id + " Thread Finished");
-
- }
-
- }
-
- private Map<Long, Long> userPullInfo;
-
- protected boolean shouldContinuePulling(List<MediaFeedData> statuses) {
- return (statuses != null) && (statuses.size() > 0);
- }
-
- private void sleep()
- {
- Thread.yield();
- try {
- // wait one tenth of a millisecond
- Thread.yield();
- Thread.sleep(1);
- Thread.yield();
- }
- catch(IllegalArgumentException e) {
- // passing in static values, this will never happen
- }
- catch(InterruptedException e) {
- // noOp, there must have been an issue sleeping
- }
- Thread.yield();
- }
-
- public StreamsResultSet readCurrent() {
-
- LOGGER.info("Providing {} docs", providerQueue.size());
-
- StreamsResultSet result;
-
- try {
- lock.writeLock().lock();
- result = new StreamsResultSet(providerQueue);
- result.setCounter(new DatumStatusCounter());
- providerQueue = constructQueue();
- } finally {
- lock.writeLock().unlock();
- }
-
- if( providerQueue.isEmpty() && executor.isTerminated()) {
- LOGGER.info("Finished. Cleaning up...");
-
- running.set(false);
-
- LOGGER.info("Exiting");
- }
-
- return result;
-
- }
-
- protected Queue<StreamsDatum> constructQueue() {
- return Queues.synchronizedQueue(new LinkedBlockingQueue<StreamsDatum>(MAX_NUMBER_WAITING));
- }
-
- public StreamsResultSet readNew(BigInteger sequence) {
- LOGGER.debug("{} readNew", STREAMS_ID);
- throw new NotImplementedException();
- }
-
- public StreamsResultSet readRange(DateTime start, DateTime end) {
- LOGGER.debug("{} readRange", STREAMS_ID);
- throw new NotImplementedException();
- }
-
- @Override
- public boolean isRunning() {
- return running.get();
- }
-
- void shutdownAndAwaitTermination(ExecutorService pool) {
- pool.shutdown(); // Disable new tasks from being submitted
- try {
- // Wait a while for existing tasks to terminate
- if (!pool.awaitTermination(10, TimeUnit.SECONDS)) {
- pool.shutdownNow(); // Cancel currently executing tasks
- // Wait a while for tasks to respond to being cancelled
- if (!pool.awaitTermination(10, TimeUnit.SECONDS))
- System.err.println("Pool did not terminate");
- }
- } catch (InterruptedException ie) {
- // (Re-)Cancel if current thread also interrupted
- pool.shutdownNow();
- // Preserve interrupt status
- Thread.currentThread().interrupt();
- }
- }
-
-
- @Override
- public void prepare(Object o) {
-
- executor = getExecutor();
- running.set(true);
- try {
- lock.writeLock().lock();
- providerQueue = constructQueue();
- } finally {
- lock.writeLock().unlock();
- }
-
- Preconditions.checkNotNull(providerQueue);
-
- Preconditions.checkNotNull(this.klass);
- Preconditions.checkNotNull(config.getAccessToken());
-
- //idsCount = config.getFollow().size();
-
- client = getInstagramClient();
- }
-
- protected Instagram getInstagramClient()
- {
- // twitter4j -> jInstagram
-// String baseUrl = "https://api.instagram.com:443/1.1/";
-//
-// ConfigurationBuilder builder = new ConfigurationBuilder()
-// .setOAuthConsumerKey(config.getOauth().getConsumerKey())
-// .setOAuthConsumerSecret(config.getOauth().getConsumerSecret())
-// .setOAuthAccessToken(config.getOauth().getAccessToken())
-// .setOAuthAccessTokenSecret(config.getOauth().getAccessTokenSecret())
-// .setIncludeEntitiesEnabled(includeEntitiesEnabled)
-// .setJSONStoreEnabled(jsonStoreEnabled)
-// .setAsyncNumThreads(3)
-// .setRestBaseURL(baseUrl)
-// .setIncludeMyRetweetEnabled(Boolean.TRUE)
-// .setPrettyDebugEnabled(Boolean.TRUE);
-//
-// return new InstagramFactory(builder.build()).getInstance();
- return null;
- }
-
- @Override
- public void cleanUp() {
- shutdownAndAwaitTermination(executor);
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
index 18a59b9..f8f8117 100644
--- a/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
+++ b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramConfiguration.json
@@ -5,16 +5,17 @@
"javaType" : "org.apache.streams.instagram.InstagramConfiguration",
"javaInterfaces": ["java.io.Serializable"],
"properties": {
- "version": {
+ "clientId": {
"type": "string",
- "description": "The version"
+ "description": "Your Instagram Client Id"
},
- "endpoint": {
+ "clientSecret": {
"type": "string",
- "description": "The endpoint"
+ "description": "Your Instagram Client secret"
},
- "accessToken": {
- "type": "string"
+ "callbackUrl": {
+ "type": "string",
+ "description": "Your Instagream callback url"
}
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
index 4b75ee4..b6a8c6b 100644
--- a/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
+++ b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/InstagramUserInformationConfiguration.json
@@ -6,7 +6,7 @@
"extends": {"$ref":"InstagramConfiguration.json"},
"javaInterfaces": ["java.io.Serializable"],
"properties": {
- "info": {
+ "userIds": {
"type": "array",
"description": "A list of user IDs, indicating the users whose posts should be delivered on the stream",
"items": {
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
new file mode 100644
index 0000000..39f607c
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
@@ -0,0 +1,161 @@
+package org.apache.streams.instagram.provider;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Queues;
+import com.google.common.collect.Sets;
+import org.apache.streams.instagram.InstagramUserInformationConfiguration;
+import org.jinstagram.Instagram;
+import org.jinstagram.entity.common.Pagination;
+import org.jinstagram.entity.users.feed.MediaFeed;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.jinstagram.exceptions.InstagramException;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link org.apache.streams.instagram.provider.InstagramRecentMediaCollector}
+ */
+public class InstagramRecentMediaCollectorTest {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(InstagramRecentMediaCollectorTest.class);
+
+ private int expectedDataCount = 0;
+ private long randomSeed = System.currentTimeMillis();
+ private Random rand = new Random(randomSeed);
+ private Map<Pagination, MediaFeed> pageMap = Maps.newHashMap();
+
+ @Test
+ public void testHandleInstagramException1() throws InstagramException {
+ InstagramException ie = mock(InstagramException.class);
+ when(ie.getRemainingLimitStatus()).thenReturn(1);
+ final String message = "Test Message";
+ when(ie.getMessage()).thenReturn(message);
+ InstagramRecentMediaCollector collector = new InstagramRecentMediaCollector(new ConcurrentLinkedQueue<MediaFeedData>(), new InstagramUserInformationConfiguration());
+ try {
+ collector.handleInstagramException(ie, 1);
+ fail("Expected RuntimeException to be thrown");
+ } catch (InstagramException rte) {
+// assertTrue(rte.getMessage().contains("Mock for InstagramException"));
+ assertEquals(message, rte.getMessage());
+ }
+ }
+
+ @Test
+ public void testHandleInstagramException2() throws InstagramException{
+ InstagramException ie = mock(InstagramException.class);
+ when(ie.getRemainingLimitStatus()).thenReturn(0);
+ InstagramRecentMediaCollector collector = new InstagramRecentMediaCollector(new ConcurrentLinkedQueue<MediaFeedData>(), new InstagramUserInformationConfiguration());
+ long startTime = System.currentTimeMillis();
+ collector.handleInstagramException(ie, 1);
+ long endTime = System.currentTimeMillis();
+ LOGGER.debug("Slept for {} ms", startTime - endTime);
+ assertTrue(endTime - startTime >= 4000); //allow for 1 sec of error
+ startTime = System.currentTimeMillis();
+ collector.handleInstagramException(ie, 2);
+ endTime = System.currentTimeMillis();
+ LOGGER.debug("Slept for {} ms", startTime - endTime);
+ assertTrue(endTime - startTime >= 24000); //allow for 1 sec of error
+ }
+
+ @Test
+ public void testGetUserIds() {
+ InstagramUserInformationConfiguration config = new InstagramUserInformationConfiguration();
+ List<String> userIds = Lists.newLinkedList();
+ userIds.add("1");
+ userIds.add("2");
+ userIds.add("3");
+ userIds.add("4");
+ userIds.add("abcdefg");
+ config.setUserIds(userIds);
+ InstagramRecentMediaCollector collector = new InstagramRecentMediaCollector(new ConcurrentLinkedQueue<MediaFeedData>(), config);
+
+ Set<Long> expected = Sets.newHashSet();
+ expected.add(1L);
+ expected.add(2L);
+ expected.add(3L);
+ expected.add(4L);
+
+ assertEquals(expected, collector.getUserIds());
+ }
+
+ @Test
+ public void testRun() {
+ Queue<MediaFeedData> data = Queues.newConcurrentLinkedQueue();
+ InstagramUserInformationConfiguration config = new InstagramUserInformationConfiguration();
+ List<String> userIds = Lists.newLinkedList();
+ userIds.add("1");
+ userIds.add("2");
+ userIds.add("3");
+ userIds.add("4");
+ config.setUserIds(userIds);
+ InstagramRecentMediaCollector collector = new InstagramRecentMediaCollector(data, config);
+ collector.setInstagramClient(createMockInstagramClient());
+ collector.run();
+ LOGGER.debug("Random seed == {}", randomSeed);
+ assertEquals("Random Seed == " + randomSeed, this.expectedDataCount, data.size());
+ }
+
+ private Instagram createMockInstagramClient() {
+ final Instagram instagramClient = mock(Instagram.class);
+ try {
+ final InstagramException mockException = mock(InstagramException.class);
+ when(mockException.getRemainingLimitStatus()).thenReturn(-1);
+ when(mockException.getMessage()).thenReturn("MockInstagramException message");
+ when(instagramClient.getRecentMediaFeed(any(Long.class))).thenAnswer(new Answer<MediaFeed>() {
+ @Override
+ public MediaFeed answer(InvocationOnMock invocationOnMock) throws Throwable {
+ long param = (Long) invocationOnMock.getArguments()[0];
+ if (param == 2L) {
+ throw mockException;
+ } else {
+ return createRandomMockMediaFeed();
+ }
+ }
+ });
+ when(instagramClient.getRecentMediaNextPage(any(Pagination.class))).thenAnswer(new Answer<MediaFeed>() {
+ @Override
+ public MediaFeed answer(InvocationOnMock invocationOnMock) throws Throwable {
+ return createRandomMockMediaFeed();
+ }
+ });
+ } catch (InstagramException ie) {
+ fail("Failed to create mock instagram client.");
+ }
+ return instagramClient;
+ }
+
+ private MediaFeed createRandomMockMediaFeed() throws InstagramException {
+ MediaFeed feed = mock(MediaFeed.class);
+ when(feed.getData()).thenReturn(createData(this.rand.nextInt(100)));
+ Pagination pagination = mock(Pagination.class);
+ if(this.rand.nextInt(2) == 0) {
+ when(pagination.hasNextPage()).thenReturn(true);
+ } else {
+ when(pagination.hasNextPage()).thenReturn(false);
+ }
+ when(feed.getPagination()).thenReturn(pagination);
+ return feed;
+ }
+
+ private List<MediaFeedData> createData(int size) {
+ List<MediaFeedData> data = Lists.newLinkedList();
+ for(int i=0; i < size; ++i) {
+ data.add(mock(MediaFeedData.class));
+ }
+ this.expectedDataCount += size;
+ return data;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
new file mode 100644
index 0000000..7d2a47b
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
@@ -0,0 +1,145 @@
+package org.apache.streams.instagram.provider;
+
+import org.apache.streams.core.StreamsResultSet;
+import org.apache.streams.instagram.InstagramUserInformationConfiguration;
+import org.jinstagram.entity.users.feed.MediaFeedData;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Random;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+
+/**
+ *
+ */
+public class InstagramRecentMediaProviderTest {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(InstagramRecentMediaProviderTest.class);
+
+ @Test
+ public void testStartStream() throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final InstagramRecentMediaCollector collectorStub = new InstagramRecentMediaCollector(new ConcurrentLinkedQueue<MediaFeedData>(), new InstagramUserInformationConfiguration()) {
+
+ private volatile boolean isFinished = false;
+
+ @Override
+ public void run() {
+ this.isFinished = true;
+ latch.countDown();
+ }
+
+ @Override
+ public boolean isCompleted() {
+ return this.isFinished;
+ }
+ };
+
+ InstagramRecentMediaProvider provider = new InstagramRecentMediaProvider(null) {
+ @Override
+ protected InstagramRecentMediaCollector getInstagramRecentMediaCollector() {
+ return collectorStub;
+ }
+ };
+
+ provider.startStream();
+
+ latch.await();
+ assertTrue(collectorStub.isCompleted());
+ StreamsResultSet result = provider.readCurrent();
+ assertNotNull(result);
+ assertEquals(0, result.size());
+ assertTrue(!provider.isRunning());
+ try {
+ provider.cleanUp();
+ } catch (Throwable throwable){
+ throwable.printStackTrace();
+ fail("Error durring clean up");
+ }
+ }
+
+ @Test
+ public void testReadCurrent() {
+ final long seed = System.nanoTime();
+ final Random rand = new Random(seed);
+ final CyclicBarrier test = new CyclicBarrier(2);
+ final CyclicBarrier produce = new CyclicBarrier(2);
+ final AtomicInteger batchCount = new AtomicInteger(0);
+ InstagramRecentMediaProvider provider = new InstagramRecentMediaProvider(new InstagramUserInformationConfiguration()) {
+ @Override
+ protected InstagramRecentMediaCollector getInstagramRecentMediaCollector() {
+ return new InstagramRecentMediaCollector(super.mediaFeedQueue, new InstagramUserInformationConfiguration()) {
+
+ private volatile boolean isFinished = false;
+
+
+
+ public int getBatchCount() {
+ return batchCount.get();
+ }
+
+ @Override
+ public boolean isCompleted() {
+ return isFinished;
+ }
+
+ @Override
+ public void run() {
+ int randInt = rand.nextInt(5);
+ while(randInt != 0) {
+ int batchSize = rand.nextInt(200);
+ for(int i=0; i < batchSize; ++i) {
+ while(!super.dataQueue.add(mock(MediaFeedData.class))) {
+ Thread.yield();
+ }
+ }
+ batchCount.set(batchSize);
+ try {
+ test.await();
+ produce.await();
+ } catch (InterruptedException ie ) {
+ Thread.currentThread().interrupt();
+ } catch (BrokenBarrierException bbe) {
+ Thread.currentThread().interrupt();
+ }
+ randInt = rand.nextInt(5);
+ }
+ batchCount.set(0);
+ isFinished = true;
+ try {
+ test.await();
+ produce.await();
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ } catch (BrokenBarrierException bbe) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ };
+ }
+ };
+ provider.startStream();
+ while(provider.isRunning()) {
+ try {
+ test.await();
+ assertEquals("Seed == "+seed, batchCount.get(), provider.readCurrent().size());
+ produce.await();
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ } catch (BrokenBarrierException bbe) {
+ Thread.currentThread().interrupt();
+ }
+
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/77603934/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
deleted file mode 100644
index fcf5e81..0000000
--- a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/twitter/test/InstagramActivitySerDeTest.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.apache.streams.twitter.test;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.apache.commons.lang.StringUtils;
-import org.apache.streams.instagram.serializer.InstagramJsonActivitySerializer;
-import org.apache.streams.jackson.StreamsJacksonMapper;
-import org.apache.streams.pojo.json.Activity;
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.assertThat;
-
-/**
-* Created with IntelliJ IDEA.
-* User: sblackmon
-* Date: 8/20/13
-* Time: 5:57 PM
-* To change this template use File | Settings | File Templates.
-*/
-public class InstagramActivitySerDeTest {
-
- private final static Logger LOGGER = LoggerFactory.getLogger(InstagramActivitySerDeTest.class);
- private ObjectMapper mapper = StreamsJacksonMapper.getInstance();
-
- private InstagramJsonActivitySerializer instagramJsonActivitySerializer = new InstagramJsonActivitySerializer();
-
- // remove @Ignore after implementation
- @Ignore
- @Test
- public void Tests()
- {
- InputStream is = InstagramActivitySerDeTest.class.getResourceAsStream("/test.txt");
- InputStreamReader isr = new InputStreamReader(is);
- BufferedReader br = new BufferedReader(isr);
-
- try {
- while (br.ready()) {
- String line = br.readLine();
- if(!StringUtils.isEmpty(line))
- {
- LOGGER.info("raw: {}", line);
-
- // convert to MediaFeedData?
- Activity activity = instagramJsonActivitySerializer.deserialize(line);
-
- String activitystring = mapper.writeValueAsString(activity);
-
- LOGGER.info("activity: {}", activitystring);
-
- assertThat(activity, is(not(nullValue())));
-
- assertThat(activity.getId(), is(not(nullValue())));
- assertThat(activity.getActor(), is(not(nullValue())));
- assertThat(activity.getActor().getId(), is(not(nullValue())));
- assertThat(activity.getVerb(), is(not(nullValue())));
- assertThat(activity.getProvider(), is(not(nullValue())));
-
- }
- }
- } catch( Exception e ) {
- System.out.println(e);
- e.printStackTrace();
- Assert.fail();
- }
- }
-}
[05/18] git commit: Merge branch 'instagram' into STREAMS-122
Posted by sb...@apache.org.
Merge branch 'instagram' into STREAMS-122
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/7b301cea
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/7b301cea
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/7b301cea
Branch: refs/heads/master
Commit: 7b301ceaa358e6c572b95a0eff754a87cf9f6064
Parents: 3b98df8 a3f443f
Author: Robert Douglas <rd...@w2odigital.com>
Authored: Tue Jul 1 17:11:28 2014 -0500
Committer: Robert Douglas <rd...@w2odigital.com>
Committed: Tue Jul 1 17:11:28 2014 -0500
----------------------------------------------------------------------
.../streams-provider-instagram/README.md | 17 +
.../InstagramMediaDataActivitySerializer.mup | 894 +++++++++++++++++++
.../metadata/instagram_to_activity_mapping.png | Bin 0 -> 1123684 bytes
.../streams-provider-instagram/pom.xml | 139 +++
.../instagram/InstagramConfigurator.java | 78 ++
.../processor/InstagramTypeConverter.java | 190 ++++
.../provider/InstagramTimelineProvider.java | 409 +++++++++
.../InstagramJsonActivitySerializer.java | 60 ++
.../serializer/util/InstagramActivityUtil.java | 142 +++
.../serializer/util/InstagramDeserializer.java | 18 +
.../jsonschema/com/instagram/Instagram.json | 0
.../com/instagram/InstagramConfiguration.json | 20 +
.../InstagramUserInformationConfiguration.json | 17 +
.../src/main/resources/reference.conf | 5 +
.../test/InstagramActivitySerDeTest.java | 92 ++
.../src/test/resources/testMediaFeedObjects.txt | 0
.../src/test/resources/testtweets.txt | 695 ++++++++++++++
17 files changed, 2776 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/7b301cea/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
----------------------------------------------------------------------
diff --cc streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
index 0000000,0000000..b4aee2d
new file mode 100644
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramDeserializer.java
@@@ -1,0 -1,0 +1,18 @@@
++package org.apache.streams.instagram.serializer.util;
++
++import org.jinstagram.Instagram;
++import org.jinstagram.exceptions.InstagramException;
++
++/**
++ * Created by rdouglas on 7/1/14.
++ */
++public class InstagramDeserializer extends Instagram{
++ public InstagramDeserializer(String test) {
++ super(test);
++ }
++
++ @Override
++ public <T> T createObjectFromResponse(Class<T> clazz, String response) throws InstagramException {
++ return super.createObjectFromResponse(clazz, response);
++ }
++}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/7b301cea/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/Instagram.json
----------------------------------------------------------------------
diff --cc streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/Instagram.json
index 0000000,0000000..e69de29
new file mode 100644
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/main/jsonschema/com/instagram/Instagram.json
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/7b301cea/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
----------------------------------------------------------------------
diff --cc streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
index 0000000,0000000..e69de29
new file mode 100644
--- /dev/null
+++ b/streams-contrib/streams-provider-instagram/src/test/resources/testMediaFeedObjects.txt
[13/18] git commit: STREAMS 121 | Added javadoc comments
Posted by sb...@apache.org.
STREAMS 121 | Added javadoc comments
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/95f6d5d9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/95f6d5d9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/95f6d5d9
Branch: refs/heads/master
Commit: 95f6d5d9bc9069258717a9880e491ce55ae343e2
Parents: 7760393
Author: rebanks <re...@w2odigital.com>
Authored: Fri Jul 11 16:29:55 2014 -0500
Committer: rebanks <re...@w2odigital.com>
Committed: Fri Jul 11 16:29:55 2014 -0500
----------------------------------------------------------------------
.../provider/InstagramRecentMediaCollector.java | 32 +++++++++++++++++++-
.../provider/InstagramRecentMediaProvider.java | 6 +++-
2 files changed, 36 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/95f6d5d9/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
index 7eb3fcd..928ff9e 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
@@ -13,6 +13,11 @@ import java.util.Queue;
import java.util.Set;
/**
+ * Executes on all of the Instagram requests to collect the media feed data.
+ *
+ * If errors/exceptions occur when trying to gather data for a particular user, that user is skipped and the collector
+ * move on to the next user. If a rate limit exception occurs it employs an exponential back off strategy for up to
+ * 5 attempts.
*
*/
public class InstagramRecentMediaCollector implements Runnable {
@@ -34,10 +39,19 @@ public class InstagramRecentMediaCollector implements Runnable {
this.isCompleted = false;
}
+ /**
+ * Set instagram client
+ * @param instagramClient
+ */
protected void setInstagramClient(Instagram instagramClient) {
this.instagramClient = instagramClient;
}
+ /**
+ * Gets the user ids from the {@link org.apache.streams.instagram.InstagramUserInformationConfiguration} and
+ * converts them to {@link java.lang.Long}
+ * @return
+ */
protected Set<Long> getUserIds() {
Set<Long> userIds = Sets.newHashSet();
for(String id : config.getUserIds()) {
@@ -50,6 +64,14 @@ public class InstagramRecentMediaCollector implements Runnable {
return userIds;
}
+ /**
+ * Determins the course of action to take when Instagram returns an exception to a request. If it is a rate limit
+ * exception, it implements an exponentional back off strategy. If it is anyother exception, it is logged and
+ * rethrown.
+ * @param instaExec exception to handle
+ * @param attempt number of attempts that have occured to pull this users information
+ * @throws InstagramException
+ */
protected void handleInstagramException(InstagramException instaExec, int attempt) throws InstagramException {
LOGGER.debug("RemainingApiLimitStatus: {}", instaExec.getRemainingLimitStatus());
if(instaExec.getRemainingLimitStatus() == 0) { //rate limit exception
@@ -66,6 +88,10 @@ public class InstagramRecentMediaCollector implements Runnable {
}
}
+ /**
+ * Gets the MediaFeedData for this particular user and adds it to the share queued.
+ * @param userId
+ */
private void getUserMedia(Long userId) {
MediaFeed feed = null;
int attempts = 0;
@@ -97,7 +123,7 @@ public class InstagramRecentMediaCollector implements Runnable {
LOGGER.error("User id, {}, returned a NULL media feed from instagram.", userId);
} else {
for(MediaFeedData data : userFeed.getData()) {
- synchronized (this.dataQueue) {
+ synchronized (this.dataQueue) { //unnecessary
while(!this.dataQueue.offer(data)) {
Thread.yield();
}
@@ -106,6 +132,10 @@ public class InstagramRecentMediaCollector implements Runnable {
}
}
+ /**
+ *
+ * @return true when the collector has queued all of available media feed data for the provided users.
+ */
public boolean isCompleted() {
return this.isCompleted;
}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/95f6d5d9/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
index 3354e54..e5fa464 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
@@ -17,7 +17,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
- * Created by rebanks on 7/9/14.
+ * Instagram {@link org.apache.streams.core.StreamsProvider} that provides the recent media data for a group of users
*/
public class InstagramRecentMediaProvider implements StreamsProvider {
@@ -44,6 +44,10 @@ public class InstagramRecentMediaProvider implements StreamsProvider {
this.executorService.submit(this.dataCollector);
}
+ /**
+ * EXPOSED FOR TESTING
+ * @return
+ */
protected InstagramRecentMediaCollector getInstagramRecentMediaCollector() {
return new InstagramRecentMediaCollector(this.mediaFeedQueue, this.config);
}
[08/18] git commit: Merge branch 'master' into instagram
Posted by sb...@apache.org.
Merge branch 'master' into instagram
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/815ce2ab
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/815ce2ab
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/815ce2ab
Branch: refs/heads/master
Commit: 815ce2abdab1be8e279b48a7e38090f5f6948fcb
Parents: a3f443f c1246d9
Author: mfranklin <mf...@apache.org>
Authored: Wed Jul 2 12:13:35 2014 -0400
Committer: mfranklin <mf...@apache.org>
Committed: Wed Jul 2 12:13:35 2014 -0400
----------------------------------------------------------------------
.../ElasticsearchConfigurator.java | 4 +
.../ElasticsearchPersistUpdater.java | 35 -
.../ElasticsearchPersistWriter.java | 705 +++++++------------
.../ElasticsearchPersistWriterTask.java | 56 --
.../ElasticsearchWriterConfiguration.json | 5 +
.../provider/SysomosHeartbeatStream.java | 2 +-
.../twitter/processor/TwitterTypeConverter.java | 2 +-
.../TwitterJsonActivitySerializer.java | 3 +
.../TwitterJsonUserActivitySerializer.java | 72 ++
.../serializer/util/TwitterActivityUtil.java | 22 +
.../local/tasks/StreamsProviderTask.java | 51 +-
.../local/builders/LocalStreamBuilderTest.java | 52 +-
.../local/test/processors/SlowProcessor.java | 50 ++
.../test/providers/EmptyResultSetProvider.java | 2 +-
.../test/providers/NumericMessageProvider.java | 63 +-
.../test/component/FileReaderProvider.java | 52 +-
16 files changed, 517 insertions(+), 659 deletions(-)
----------------------------------------------------------------------
[07/18] git commit: STREAMS-105 | Updated the InstagramTypeConverter
to use the conversion utility functions provided in InstagramActivityUtil
Posted by sb...@apache.org.
STREAMS-105 | Updated the InstagramTypeConverter to use the conversion utility functions provided in InstagramActivityUtil
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/fa3f9220
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/fa3f9220
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/fa3f9220
Branch: refs/heads/master
Commit: fa3f92200d19605e931954a154e59123d1a36f03
Parents: 1163653
Author: Robert Douglas <rd...@w2odigital.com>
Authored: Wed Jul 2 10:48:35 2014 -0500
Committer: Robert Douglas <rd...@w2odigital.com>
Committed: Wed Jul 2 10:48:35 2014 -0500
----------------------------------------------------------------------
.../processor/InstagramTypeConverter.java | 116 +++----------------
.../serializer/util/InstagramActivityUtil.java | 32 +++--
2 files changed, 34 insertions(+), 114 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/fa3f9220/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
index 14260e3..7fb1ec6 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/processor/InstagramTypeConverter.java
@@ -18,52 +18,34 @@
package org.apache.streams.instagram.processor;
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;
import org.apache.streams.core.StreamsDatum;
import org.apache.streams.core.StreamsProcessor;
-import org.apache.streams.exceptions.ActivitySerializerException;
-import org.apache.streams.instagram.serializer.InstagramJsonActivitySerializer;
-import org.apache.streams.jackson.StreamsJacksonMapper;
+import org.apache.streams.instagram.serializer.util.InstagramActivityUtil;
import org.apache.streams.pojo.json.Activity;
import org.jinstagram.entity.users.feed.MediaFeedData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.io.IOException;
import java.util.List;
import java.util.Queue;
-/**
- * Created by sblackmon on 12/10/13.
- */
public class InstagramTypeConverter implements StreamsProcessor {
public final static String STREAMS_ID = "InstagramTypeConverter";
private final static Logger LOGGER = LoggerFactory.getLogger(InstagramTypeConverter.class);
- private ObjectMapper mapper;
-
private Queue<MediaFeedData> inQueue;
private Queue<StreamsDatum> outQueue;
- private Class inClass;
- private Class outClass;
-
- private InstagramJsonActivitySerializer instagramJsonActivitySerializer;
+ private InstagramActivityUtil instagramActivityUtil;
private int count = 0;
public final static String TERMINATE = new String("TERMINATE");
- public InstagramTypeConverter(Class inClass, Class outClass) {
- this.inClass = inClass;
- this.outClass = outClass;
+ public InstagramTypeConverter() {
}
public Queue<StreamsDatum> getProcessorOutputQueue() {
@@ -74,100 +56,31 @@ public class InstagramTypeConverter implements StreamsProcessor {
inQueue = inputQueue;
}
- public Object convert(ObjectNode event, Class inClass, Class outClass) throws ActivitySerializerException, JsonProcessingException {
-
- Object result = null;
-
- if( outClass.equals( Activity.class )) {
- LOGGER.debug("ACTIVITY");
- result = instagramJsonActivitySerializer.deserialize(
- mapper.writeValueAsString(event));
- } else if( outClass.equals( ObjectNode.class )) {
- LOGGER.debug("OBJECTNODE");
- result = mapper.convertValue(event, ObjectNode.class);
- } else if( outClass.equals( String.class )) {
- LOGGER.debug("OBJECTNODE");
- result = mapper.writeValueAsString(event);
- }
-
-
- // no supported conversion were applied
- if( result != null ) {
- count ++;
- return result;
- }
-
- LOGGER.debug("CONVERT FAILED");
-
- return null;
-
- }
-
- public boolean validate(Object document, Class klass) {
-
- // TODO
- return true;
- }
-
- public boolean isValidJSON(final String json) {
- boolean valid = false;
- try {
- final JsonParser parser = new ObjectMapper().getJsonFactory()
- .createJsonParser(json);
- while (parser.nextToken() != null) {
- }
- valid = true;
- } catch (JsonParseException jpe) {
- LOGGER.warn("validate: {}", jpe);
- } catch (IOException ioe) {
- LOGGER.warn("validate: {}", ioe);
- }
-
- return valid;
- }
-
@Override
public List<StreamsDatum> process(StreamsDatum entry) {
StreamsDatum result = null;
try {
-
Object item = entry.getDocument();
- ObjectNode node;
LOGGER.debug("{} processing {}", STREAMS_ID, item.getClass());
- if( item instanceof String ) {
-
- // if the target is string, just pass-through
- if( String.class.equals(outClass)) {
- result = entry;
- }
- else {
- // first check for valid json
- node = (ObjectNode)mapper.readTree((String)item);
+ if(item instanceof MediaFeedData) {
+ //We don't need to use the mapper, since we have a process to convert between
+ //MediaFeedData objects and Activity objects already
+ Activity activity = new Activity();
- Object out = convert(node, String.class, outClass);
+ instagramActivityUtil.updateActivity((MediaFeedData)item, activity);
- if( out != null && validate(out, outClass))
- result = new StreamsDatum(out);
+ if(activity.getId() != null) {
+ result = new StreamsDatum(activity);
+ count++;
}
-
- } else if( item instanceof ObjectNode ) {
-
- // first check for valid json
- node = (ObjectNode)mapper.valueToTree(item);
-
- Object out = convert(node, ObjectNode.class, outClass);
-
- if( out != null && validate(out, outClass))
- result = new StreamsDatum(out);
-
}
-
} catch (Exception e) {
e.printStackTrace();
+ LOGGER.error("Exception while converting MediaFeedData to Activity: {}", e.getMessage());
}
if( result != null )
@@ -178,13 +91,12 @@ public class InstagramTypeConverter implements StreamsProcessor {
@Override
public void prepare(Object o) {
- mapper = new StreamsJacksonMapper();
- instagramJsonActivitySerializer = new InstagramJsonActivitySerializer();
+ instagramActivityUtil = new InstagramActivityUtil();
}
@Override
public void cleanUp() {
-
+ //noop
}
}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/fa3f9220/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
index 0561ba7..bd926d5 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
@@ -53,7 +53,9 @@ public class InstagramActivityUtil {
*/
public static void updateActivity(MediaFeedData item, Activity activity) throws ActivitySerializerException {
activity.setActor(buildActor(item));
- activity.setPublished(new DateTime(Long.parseLong(item.getCreatedTime()) * 1000));
+
+ if(item.getCreatedTime() != null)
+ activity.setPublished(new DateTime(Long.parseLong(item.getCreatedTime()) * 1000));
activity.setId(formatId(activity.getVerb(),
Optional.fromNullable(
@@ -78,16 +80,20 @@ public class InstagramActivityUtil {
public static Actor buildActor(MediaFeedData item) {
Actor actor = new Actor();
- Image image = new Image();
- image.setUrl(item.getUser().getProfilePictureUrl());
+ try {
+ Image image = new Image();
+ image.setUrl(item.getUser().getProfilePictureUrl());
- Map<String, Object> extensions = new HashMap<String, Object>();
- extensions.put("screenName", item.getUser().getUserName());
+ Map<String, Object> extensions = new HashMap<String, Object>();
+ extensions.put("screenName", item.getUser().getUserName());
- actor.setId(formatId(String.valueOf(item.getUser().getId())));
- actor.setImage(image);
- actor.setAdditionalProperty("extensions", extensions);
- actor.setAdditionalProperty("handle", item.getUser().getUserName());
+ actor.setId(formatId(String.valueOf(item.getUser().getId())));
+ actor.setImage(image);
+ actor.setAdditionalProperty("extensions", extensions);
+ actor.setAdditionalProperty("handle", item.getUser().getUserName());
+ } catch (Exception e) {
+ LOGGER.error("Exception trying to build actor object: {}", e.getMessage());
+ }
return actor;
}
@@ -244,9 +250,11 @@ public class InstagramActivityUtil {
addLocationExtension(activity, item);
- Map<String, Object> likes = new HashMap<String, Object>();
- likes.put("count", item.getLikes().getCount());
- extensions.put("likes", likes);
+ if(item.getLikes() != null) {
+ Map<String, Object> likes = new HashMap<String, Object>();
+ likes.put("count", item.getLikes().getCount());
+ extensions.put("likes", likes);
+ }
extensions.put("hashtags", item.getTags());
[11/18] git commit: STREAMS-122 | Responded to code review feedback
Posted by sb...@apache.org.
STREAMS-122 | Responded to code review feedback
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/14f7050b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/14f7050b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/14f7050b
Branch: refs/heads/master
Commit: 14f7050ba7de9bd783e6b2bf9c1dcf76da2d9eb2
Parents: 957e13f
Author: Robert Douglas <rd...@w2odigital.com>
Authored: Mon Jul 7 11:08:17 2014 -0500
Committer: Robert Douglas <rd...@w2odigital.com>
Committed: Mon Jul 7 11:08:17 2014 -0500
----------------------------------------------------------------------
.../serializer/util/InstagramActivityUtil.java | 45 ++++++++++++--------
1 file changed, 27 insertions(+), 18 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/14f7050b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
index bd926d5..499d0e7 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/serializer/util/InstagramActivityUtil.java
@@ -24,10 +24,8 @@ import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import org.apache.streams.exceptions.ActivitySerializerException;
import org.apache.streams.pojo.json.*;
-import org.jinstagram.entity.common.ImageData;
-import org.jinstagram.entity.common.Images;
-import org.jinstagram.entity.common.VideoData;
-import org.jinstagram.entity.common.Videos;
+import org.jinstagram.entity.comments.CommentData;
+import org.jinstagram.entity.common.*;
import org.jinstagram.entity.users.feed.MediaFeedData;
import org.joda.time.DateTime;
import org.slf4j.Logger;
@@ -53,6 +51,7 @@ public class InstagramActivityUtil {
*/
public static void updateActivity(MediaFeedData item, Activity activity) throws ActivitySerializerException {
activity.setActor(buildActor(item));
+ activity.setVerb("post");
if(item.getCreatedTime() != null)
activity.setPublished(new DateTime(Long.parseLong(item.getCreatedTime()) * 1000));
@@ -109,6 +108,21 @@ public class InstagramActivityUtil {
actObj.setObjectType(item.getType());
actObj.setAttachments(buildActivityObjectAttachments(item));
+ Image standardResolution = new Image();
+ if(item.getType() == "image" && item.getImages() != null) {
+ ImageData standardResolutionData = item.getImages().getStandardResolution();
+ standardResolution.setHeight((double)standardResolutionData.getImageHeight());
+ standardResolution.setWidth((double)standardResolutionData.getImageWidth());
+ standardResolution.setUrl(standardResolutionData.getImageUrl());
+ } else if(item.getType() == "video" && item.getVideos() != null) {
+ VideoData standardResolutionData = item.getVideos().getStandardResolution();
+ standardResolution.setHeight((double)standardResolutionData.getHeight());
+ standardResolution.setWidth((double)standardResolutionData.getWidth());
+ standardResolution.setUrl(standardResolutionData.getUrl());
+ }
+
+ actObj.setImage(standardResolution);
+
return actObj;
}
@@ -212,11 +226,10 @@ public class InstagramActivityUtil {
if(item.getLocation() != null) {
Map<String, Object> coordinates = new HashMap<String, Object>();
coordinates.put("type", "Point");
- coordinates.put("coordinates", "[" + item.getLocation().getLatitude() + "," + item.getLocation().getLongitude() + "]");
+ coordinates.put("coordinates", "[" + item.getLocation().getLongitude() + "," + item.getLocation().getLatitude() + "]");
extensions.put("coordinates", coordinates);
}
-
}
/**
@@ -258,19 +271,15 @@ public class InstagramActivityUtil {
extensions.put("hashtags", item.getTags());
- Image standardResolution = new Image();
- if(item.getType() == "image" && item.getImages() != null) {
- ImageData standardResolutionData = item.getImages().getStandardResolution();
- standardResolution.setHeight((double)standardResolutionData.getImageHeight());
- standardResolution.setWidth((double)standardResolutionData.getImageWidth());
- standardResolution.setUrl(standardResolutionData.getImageUrl());
- } else if(item.getType() == "video" && item.getVideos() != null) {
- VideoData standardResolutionData = item.getVideos().getStandardResolution();
- standardResolution.setHeight((double)standardResolutionData.getHeight());
- standardResolution.setWidth((double)standardResolutionData.getWidth());
- standardResolution.setUrl(standardResolutionData.getUrl());
+ Comments comments = item.getComments();
+ String commentsConcat = "";
+ for(CommentData commentData : comments.getComments()) {
+ commentsConcat += " " + commentData.getText();
+ }
+ if(item.getCaption() != null) {
+ commentsConcat += " " + item.getCaption().getText();
}
- extensions.put("image", standardResolution);
+ extensions.put("keywords", commentsConcat);
}
}
\ No newline at end of file
[15/18] git commit: Added license header and remvoed comments
Posted by sb...@apache.org.
Added license header and remvoed comments
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/45510b29
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/45510b29
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/45510b29
Branch: refs/heads/master
Commit: 45510b292b8f1dc5de5611c87abf70b856be4125
Parents: 1e23300
Author: rebanks <re...@w2odigital.com>
Authored: Mon Jul 14 10:22:47 2014 -0500
Committer: rebanks <re...@w2odigital.com>
Committed: Mon Jul 14 10:22:47 2014 -0500
----------------------------------------------------------------------
.../streams/instagram/InstagramConfigurator.java | 12 +-----------
.../provider/InstagramRecentMediaCollector.java | 14 ++++++++++++++
.../provider/InstagramRecentMediaProvider.java | 14 ++++++++++++++
.../provider/InstagramRecentMediaCollectorTest.java | 14 ++++++++++++++
.../provider/InstagramRecentMediaProviderTest.java | 14 ++++++++++++++
5 files changed, 57 insertions(+), 11 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/45510b29/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
index 4d01605..cab072d 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/InstagramConfigurator.java
@@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
- * Created by sblackmon on 12/10/13.
+ *
*/
public class InstagramConfigurator {
@@ -38,9 +38,6 @@ public class InstagramConfigurator {
public static InstagramConfiguration detectInstagramConfiguration(Config config) {
-// ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
-// Validator validator = factory.getValidator();
-
InstagramConfiguration instagramConfiguration = null;
try {
instagramConfiguration = mapper.readValue(config.root().render(ConfigRenderOptions.concise()), InstagramConfiguration.class);
@@ -49,16 +46,11 @@ public class InstagramConfigurator {
}
Preconditions.checkNotNull(instagramConfiguration);
-// Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
-
return instagramConfiguration;
}
public static InstagramUserInformationConfiguration detectInstagramUserInformationConfiguration(Config config) {
-// ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
-// Validator validator = factory.getValidator();
-
InstagramUserInformationConfiguration instagramConfiguration = null;
try {
instagramConfiguration = mapper.readValue(config.root().render(ConfigRenderOptions.concise()), InstagramUserInformationConfiguration.class);
@@ -67,8 +59,6 @@ public class InstagramConfigurator {
}
Preconditions.checkNotNull(instagramConfiguration);
-// Preconditions.checkState(validator.validate(instagramConfiguration).size() == 0);
-
return instagramConfiguration;
}
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/45510b29/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
index 928ff9e..e459a0a 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollector.java
@@ -1,3 +1,17 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance *
+http://www.apache.org/licenses/LICENSE-2.0 *
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
package org.apache.streams.instagram.provider;
import com.google.common.collect.Sets;
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/45510b29/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
index e5fa464..3d67a48 100644
--- a/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
+++ b/streams-contrib/streams-provider-instagram/src/main/java/org/apache/streams/instagram/provider/InstagramRecentMediaProvider.java
@@ -1,3 +1,17 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance *
+http://www.apache.org/licenses/LICENSE-2.0 *
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
package org.apache.streams.instagram.provider;
import com.google.common.collect.Queues;
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/45510b29/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
index 39f607c..0225b40 100644
--- a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaCollectorTest.java
@@ -1,3 +1,17 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance *
+http://www.apache.org/licenses/LICENSE-2.0 *
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
package org.apache.streams.instagram.provider;
import com.google.common.collect.Lists;
http://git-wip-us.apache.org/repos/asf/incubator-streams/blob/45510b29/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
----------------------------------------------------------------------
diff --git a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
index 7d2a47b..59e90b4 100644
--- a/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
+++ b/streams-contrib/streams-provider-instagram/src/test/java/org/apache/streams/instagram/provider/InstagramRecentMediaProviderTest.java
@@ -1,3 +1,17 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance *
+http://www.apache.org/licenses/LICENSE-2.0 *
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
package org.apache.streams.instagram.provider;
import org.apache.streams.core.StreamsResultSet;
[09/18] git commit: Merge branch 'instagram' into STREAMS-122
Posted by sb...@apache.org.
Merge branch 'instagram' into STREAMS-122
Project: http://git-wip-us.apache.org/repos/asf/incubator-streams/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-streams/commit/23bfcac8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-streams/tree/23bfcac8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-streams/diff/23bfcac8
Branch: refs/heads/master
Commit: 23bfcac82884d3280815f1168b82e263f38ef1fb
Parents: fa3f922 815ce2a
Author: Robert Douglas <rd...@w2odigital.com>
Authored: Wed Jul 2 11:17:27 2014 -0500
Committer: Robert Douglas <rd...@w2odigital.com>
Committed: Wed Jul 2 11:17:27 2014 -0500
----------------------------------------------------------------------
.../twitter/processor/TwitterTypeConverter.java | 2 +-
.../TwitterJsonActivitySerializer.java | 3 +
.../TwitterJsonUserActivitySerializer.java | 72 ++++++++++++++++++++
.../serializer/util/TwitterActivityUtil.java | 22 ++++++
4 files changed, 98 insertions(+), 1 deletion(-)
----------------------------------------------------------------------