def dist_authenticate(self): """ Authenticate the account with Google Reader """ was_authenticated = self.is_authenticated self.is_authenticated = False try: # if this account was previously authenticated, we try to get # another token if self.g_auth and was_authenticated: try: sys.stderr.write("AUTH: update token\n") self.g_auth.token = self.g_auth._getToken() sys.stderr.write("AUTH: token=%s\n" % self.g_auth.token) self.is_authenticated = True settings.set('google', 'token', self.g_auth.token, save_all=True) except: pass # else, but if we already had tokens by the past, try with them elif settings.get('google', 'auth_token') and settings.get('google', 'token'): sys.stderr.write("AUTH: load saved auth\n") self.g_auth = SavedAuth(settings.get('google', 'account'), \ settings.get('google', 'password'), \ settings.get('google', 'auth_token'), \ settings.get('google', 'token')) try: # test if the token is still valid self.g_auth.token = self.g_auth._getToken() except: pass else: # it's valid so we are authenticated settings.set('google', 'token', self.g_auth.token, save_all=True) self.is_authenticated = True # here, we have not a valid token, so we do a full authentication if not self.is_authenticated: sys.stderr.write("AUTH: full auth\n") self.g_auth = ClientAuth(settings.get('google', 'account'), settings.get('google', 'password')) self.is_authenticated = True settings.set('google', 'verified', True) settings.set('google', 'auth_token', self.g_auth.auth_token) settings.set('google', 'token', self.g_auth.token, save_all=True) # finally if we are authenticated, update, or create, a new # GoogleReadr object if self.is_authenticated: if self.g_object: self.g_object.auth = self.g_auth else: self.create_g_object() # an exception was raised during the authentication. # it is either a authentication failure, or a network failure # but let the caller manage this except: self.is_authenticated = False raise
class Account(object): """ A Google Reader account. We store it's login, it's password, and a ClientAuth object from libgreader and a list of categories. The id is the login """ _current = None def __init__(self, id=None, password=None): """ Instantiate a new Account """ if not id: id = str(settings.get('google', 'account')) self.id = id self.categories = [] self.categories_by_id = {} self.is_authenticated = False # An operation manager for this account self.operations_manager = OperationsManager(self, callbacks = { 'get_feed_content_done': self.operation_get_feed_content_done, 'get_more_feed_content_done': self.operation_get_feed_content_done, }) # ClientAuth object (from libgreader) self.g_auth = None # GoogleReader object (from libgreader) self.g_object = None # category for special feeds self.special_category = None # and one for orphan feeds self.orphan_feeds_category = None # total unread count self.unread = 0 @classmethod def get_current(cls): """ A class method returning the current account """ return cls._current def authenticate(self): """ Create an asynchronous operation to authenticate this account """ self.operations_manager.add(OperationAuthenticate(object=self)) def dist_authenticate(self): """ Authenticate the account with Google Reader """ was_authenticated = self.is_authenticated self.is_authenticated = False try: # if this account was previously authenticated, we try to get # another token if self.g_auth and was_authenticated: try: sys.stderr.write("AUTH: update token\n") self.g_auth.token = self.g_auth._getToken() sys.stderr.write("AUTH: token=%s\n" % self.g_auth.token) self.is_authenticated = True settings.set('google', 'token', self.g_auth.token, save_all=True) except: pass # else, but if we already had tokens by the past, try with them elif settings.get('google', 'auth_token') and settings.get('google', 'token'): sys.stderr.write("AUTH: load saved auth\n") self.g_auth = SavedAuth(settings.get('google', 'account'), \ settings.get('google', 'password'), \ settings.get('google', 'auth_token'), \ settings.get('google', 'token')) try: # test if the token is still valid self.g_auth.token = self.g_auth._getToken() except: pass else: # it's valid so we are authenticated settings.set('google', 'token', self.g_auth.token, save_all=True) self.is_authenticated = True # here, we have not a valid token, so we do a full authentication if not self.is_authenticated: sys.stderr.write("AUTH: full auth\n") self.g_auth = ClientAuth(settings.get('google', 'account'), settings.get('google', 'password')) self.is_authenticated = True settings.set('google', 'verified', True) settings.set('google', 'auth_token', self.g_auth.auth_token) settings.set('google', 'token', self.g_auth.token, save_all=True) # finally if we are authenticated, update, or create, a new # GoogleReadr object if self.is_authenticated: if self.g_object: self.g_object.auth = self.g_auth else: self.create_g_object() # an exception was raised during the authentication. # it is either a authentication failure, or a network failure # but let the caller manage this except: self.is_authenticated = False raise def assert_authenticated(self): """ Raise an exception if the account is not authenticated """ if not self.is_authenticated: raise NotAuthenticatedError("Account is not authenticated") def create_g_object(self): """ Will create a GoogleReader instance. Also make special category. """ # create the object from libgreader self.g_object = GoogleReader(self.g_auth) # create the category for special feeds self.g_object.makeSpecialFeeds() self.special_category = SpecialCategory(self) self.add_category(self.special_category) def add_category(self, category): """ Add an category to this account """ if not category.id in self.categories_by_id: self.categories_by_id[category.id] = category self.categories.append(category) def fetch_feeds(self, fetch_unread_content=False): """ Create an asynchronous operation to fetch feeds for this account """ self.operations_manager.add(OperationGetAccountFeeds( object=self, fetch_unread_content=fetch_unread_content)) def dist_fetch_feeds(self): """ Get google reader feeds for this account """ self.assert_authenticated() # get content from Google Reader self.g_object.buildSubscriptionList() # if no content, there is a problem : raise g_feeds = self.g_object.getFeeds() if not g_feeds: raise AccountHasNoFeedsError # add each category for g_category in self.g_object.getCategories(): category = Category.get_by_id(g_category.id, account=self) if not category: category = Category( id = g_category.id, account = self, g_category = g_category, ) self.add_category(category) else: category.g_category = g_category # add each feed total_unread = 0 for g_feed in g_feeds: total_unread += g_feed.unread feed = Feed.get_by_id(g_feed.id, account=self) if not feed: feed = Feed( id = g_feed.id, account = self, g_feed = g_feed, ) old_unread = feed.unread feed.unread = g_feed.unread feed.update_missing_unread() # now no unread and then we have ones : mark all items as read ! if old_unread and not feed.unread: for item in feed.items: item.unread = True # update feed's categories for g_category in g_feed.getCategories(): category = Category.get_by_id(g_category.id, account=self) category.add_feed(feed) # we have orphan feeds ? if self.g_object.orphanFeeds: if not self.orphan_feeds_category: self.orphan_feeds_category = OrphanFeedsCategory(self) self.add_category(self.orphan_feeds_category) for g_feed in self.g_object.orphanFeeds: feed = Feed.get_by_id(g_feed.id, account=self) self.orphan_feeds_category.add_feed(feed) # update unread counts self.special_category.special_feeds[GoogleReader.READING_LIST].unread = total_unread for feed in self.special_category.feeds: if feed.special_type not in (GoogleReader.READING_LIST, GoogleReader.READ_LIST): feed.unread = feed.g_feed.unread for category in self.categories: category.update_unread() self.update_unread() def fetch_unread_content(self): """ Fetch the unread content for all categories """ # first for normal categories for category in self.get_categories(unread_only=True)[1:]: category.fetch_content(unread_only=True) # then for the special one self.special_category.fetch_content(unread_only=True) def update_unread(self): """ Update the unread count : number of unread items in all feeds """ self.unread = self.special_category.unread return self.unread def get_categories(self, unread_only=False, order_by=None, reverse_order=None): """ Get the list of all categories, filtering only unread if asked, and ordering them as asked. order_by can be "title" or "unread" (default None, say as in GoogleReder) By default reverse_order is False for "title" and True for "unread" """ result = self.categories[:1] # special category always first to_sort = [] if unread_only: to_sort = [category for category in self.categories[1:] if category.unread] else: to_sort= self.categories[1:] if order_by == 'title': if reverse_order is None: reverse_order = False result += sorted(to_sort, key=locale_keyfunc(attrgetter('title')),\ reverse=reverse_order) elif order_by == 'unread': if reverse_order is None: reverse_order = True result += sorted(to_sort, key=attrgetter('unread'),\ reverse=reverse_order) else: result += to_sort return result def category_unread_count_changed(self, category): """ When the unread count of a category has changed, this method is called to update the total unread count """ if category != self.special_category: self.special_category.update_unread() self.update_unread() @property def normal_feeds(self): """ Return feeds excluding special ones """ return [feed for feed in Feed.get_for_account(self) if feed.__class__ == Feed] def operation_get_feed_content_done(self, operation): """ Called by the operations manager when a "get_feed_content" or "get_more_feed_content" operation is done. If the "max_fetch" parameter for this operation is not None, then we have to fetch more content for this feed. """ if operation.params.get('max_fetch', None) is not None: operation.params['object'].fetch_content_next_part( unread_only = operation.params.get('unread_only', False), max_fetch = operation.params['max_fetch'], )