Commits

Derek Payton committed 7f38812

Added providers for Github and Hacker News

  • Participants
  • Parent commits 5ab4b02

Comments (0)

Files changed (3)

 
 - Currently supports the following activity providers:
 
+    * `Github`_
+    * `Google Reader`_
+    * `Hacker News`_
+    * `Reddit`_
     * `Twitter`_
-    * `Google Reader`_
-    * `Reddit`_
 
 - Providers are implemented using a simple, common interface, making it very
   easy to add support for additional networks
 
 - Twitter support depends on python-twitter_
 
-- Google Reader and Reddit support depend on feedparser_ (version 4.1)
+- Google Reader, Reddit, and Github support depend on feedparser_ (version 4.1)
 
+- Hacker News support depends on iHackerNews_
 
 Installation
 ============
 - Add desired providers to ``ACTIVITYSYNC_PROVIDERS`` setting::
 
     ACTIVITYSYNC_PROVIDERS = (
+        'activitysync.providers.githubprovider.GithubProvider',
+        'activitysync.providers.hackernewsprovider.HackerNewsCommentsProvider',
+        'activitysync.providers.hackernewsprovider.HackerNewsSubmittedProvider',
         'activitysync.providers.googlereader.GoogleReaderProvider',
         'activitysync.providers.twitterprovider.TwitterUserProvider',
         'activitysync.providers.twitterprovider.TwitterSearchProvider',
   For ease of use and organizational purposes, all settings for providers should
   be stored in the ``ACTIVITYSYNC_SETTINGS`` dictionary. Settings required for
   built-in providers are::
-    
+
     ACTIVITYSYNC_SETTINGS = {
         'TWITTER_USERNAME': '', # Username to use for TwitterUserProvider
         'TWITTER_SEARCHTERM': '', # Search term to use for TwitterSearchProvider
         'REDDIT_USERNAME': '', # Username to use for RedditProvider
         'GOOGLEREADER_SHARED_RSS': '', # URL of Google Reader shared items RSS feed
         'GOOGLEREADER_PUBLIC_URL': '', # URL to Google Reader public page
+        'HACKERNEWS_USERNAME': '', # Username to use for HackerNews*Provider
+        'GITHUB_USERNAME': '', # username to use for GithubProvider
     }
 
 - Sync database to create needed models::
 .. _Bitbucket: https://bitbucket.org/dancarroll/django-activitysync
 .. _python-twitter: http://code.google.com/p/python-twitter/
 .. _feedparser: http://www.feedparser.org/
-
+.. _Github: https://github.com/
+.. _Hacker News: http://news.ycombinator.com/
+.. _iHackerNews: http://pypi.python.org/pypi/ihackernews/

File activitysync/providers/githubprovider.py

+import feedparser
+import datetime
+import time
+from activitysync.providers import ActivityProvider, ActivityInfo
+from django.conf import settings
+from ihackernews import iHackerNewsAPI, iHackerNewsException
+
+class GithubProvider(ActivityProvider):
+    """
+    Provider for Github activity
+    """
+
+    def get_activity(self):
+        item_list = []
+
+        print 'Attempting to parse Github feed'
+        username = settings.ACTIVITYSYNC_SETTINGS['GITHUB_USERNAME']
+        parsed_feed = feedparser.parse('https://github.com/%s.atom' % username)
+
+        for entry in parsed_feed.entries:
+            title = entry.title.encode(parsed_feed.encoding, 'xmlcharrefreplace')
+            guid = entry.get('id', entry.link).encode(parsed_feed.encoding, 'xmlcharrefreplace')
+            link = entry.link.encode(parsed_feed.encoding, 'xmlcharrefreplace')
+
+            if not guid:
+                guid = link
+
+            if entry.has_key('author'):
+                author = entry.author.encode(parsed_feed.encoding, 'xmlcharrefreplace')
+            else:
+                author = u''
+
+            try:
+                if entry.has_key('published_parsed'):
+                    date_published = datetime.datetime.fromtimestamp(time.mktime(entry.published_parsed) - time.timezone)
+                elif entry.has_key('updated_parsed'):
+                    date_published = datetime.datetime.fromtimestamp(time.mktime(entry.updated_parsed) - time.timezone)
+                elif entry.has_key('modified_parsed'):
+                    date_published = datetime.datetime.fromtimestamp(time.mktime(entry.modified_parsed) - time.timezone)
+                else:
+                    date_published = datetime.datetime.now()
+            except TypeError:
+                date_published = datetime.datetime.now()
+
+            activity = ActivityInfo(
+                title=title,
+                link=link,
+                pub_date=date_published,
+                guid=guid,
+                username=username,
+                author=author
+                )
+            item_list.append(activity)
+        return item_list
+
+    def name(self):
+        return 'Github'
+
+    def prefix(self):
+        return ''
+
+    def link(self):
+        return ('https://github.com/%s' %
+            settings.ACTIVITYSYNC_SETTINGS['GITHUB_USERNAME'])
+
+    def sourceid(self):
+        return 'github'

File activitysync/providers/hackernewsprovider.py

+import datetime
+import re
+from activitysync.providers import ActivityProvider, ActivityInfo
+from django.conf import settings
+from ihackernews import iHackerNewsAPI, iHackerNewsException
+
+ITEM_URL = 'http://news.ycombinator.com/item?id=%s'
+PUB_DATE_REGEX = [(re.compile(r'^(?P<num>\d+) %s ago$' % x), x) for x in ('minutes', 'hours', 'days')]
+
+class HackerNewsCommentsProvider(ActivityProvider):
+    """
+    Provider for Hacker News comments
+    """
+
+    def get_pub_date(self, pub_date):
+        for regex, arg in PUB_DATE_REGEX:
+            matched = regex.match(pub_date)
+            if matched:
+                num = int(matched.groupdict()['num'])
+                return datetime.datetime.utcnow() - datetime.timedelta(**{arg: num})
+        return None
+
+    def get_activity(self):
+        item_list = []
+
+        print 'Attempting to obtain Hacker News comments'
+        username = settings.ACTIVITYSYNC_SETTINGS['HACKERNEWS_USERNAME']
+        ' Username: %s' % username
+        hn = iHackerNewsAPI()
+        try:
+            comments = hn.threads(username).data
+        except iHackerNewsException, err:
+            print ' ihackerNews Error: %s' % err
+            return item_list
+        for comment in comments['comments']:
+            ## The API sometimes returns empty data
+            if not comment.get('id'):
+                print ' [skip] No Comment ID'
+                continue
+
+            ## Get the post details
+            try:
+                post = hn.post(comment['id']).data
+            except iHackerNewsException, err:
+                print ' [skip] iHackerNews Error: %s' % err
+                continue
+            if not post.get('id'):
+                print ' [skip] No Post ID'
+                continue
+
+            ## Parse the pub_date (x minutes ago -> datetime.datetime object)
+            pub_date = self.get_pub_date(comment['postedAgo'])
+            if pub_date is None:
+                print ' [skip] Could not parse postedAgo'
+                continue
+
+            activity = ActivityInfo(
+                title=post['title'],
+                link=ITEM_URL % comment['id'],
+                pub_date=pub_date,
+                guid='%s:%s' % (self.sourceid(), comment['id']),
+                username=username,
+                author=''
+                )
+            item_list.append(activity)
+        return item_list
+
+    def name(self):
+        return 'Hacker News'
+
+    def prefix(self):
+        return 'Commented on'
+
+    def link(self):
+        return ('http://news.ycombinator.com/user?id=%s' %
+            settings.ACTIVITYSYNC_SETTINGS['HACKERNEWS_USERNAME'])
+
+    def sourceid(self):
+        return 'hackernews'
+
+
+class HackerNewsSubmittedProvider(HackerNewsCommentsProvider):
+
+    def get_activity(self):
+        item_list = []
+
+        print 'Attempting to obtain Hacker News submitted posts'
+        username = settings.ACTIVITYSYNC_SETTINGS['HACKERNEWS_USERNAME']
+        ' Username: %s' % username
+        hn = iHackerNewsAPI()
+        try:
+            submitted = hn.by(username).data
+        except iHackerNewsException, err:
+            print ' ihackerNews Error: %s' % err
+            return item_list
+        for post in submitted['items']:
+            ## The API sometimes returns empty data
+            if not post.get('id'):
+                print ' [skip] No Post ID'
+                continue
+
+            ## Parse the pub_date (x minutes ago -> datetime.datetime object)
+            pub_date = self.get_pub_date(post['postedAgo'])
+            if pub_date is None:
+                print ' [skip] Could not parse postedAgo'
+                continue
+
+            activity = ActivityInfo(
+                title=post['title'],
+                link=ITEM_URL % post['id'],
+                pub_date=pub_date,
+                guid='%s:%s' % (self.sourceid(), post['id']),
+                username=username,
+                author=''
+                )
+            item_list.append(activity)
+        return item_list
+
+
+    def prefix(self):
+        return 'Submitted'