MobileBlur

Check-in [8cd4f18daf]
Login
Overview
Comment:Added support for multiple users to use the site simultaneously
Timelines: family | ancestors | descendants | both | develop
Files: files | file ages | folders
SHA1: 8cd4f18daf81d3c9a082dfe1e20c829005b6d598
User & Date: spiffy on 2011-11-20 20:49:00
Other Links: branch diff | manifest | tags
Context
2011-11-20
21:04
Added a catch to handle a failed Newsblur login check-in: 759f07e810 user: spiffy tags: develop
20:49
Added support for multiple users to use the site simultaneously check-in: 8cd4f18daf user: spiffy tags: develop
13:37
Merged in support for a real auth system, not a one-user DB kludge check-in: 57cc1847b3 user: spiffy tags: develop
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Modified applications/mobileblur/controllers/default.py from [b38b4efcae] to [5b9444c25f].

     1      1   from pprint import pprint
            2  +import simplejson
     2      3   
     3      4   def index():
     4      5       raw_feeds = newsblur.feeds(flat=True)["feeds"]
     5      6       feeds = {}
     6      7       for feed in raw_feeds.itervalues():
     7      8           for i in range(threshold, 2):
     8      9               if feed[thresholds[i]] > 0:
................................................................................
    15     16   def login():
    16     17       login_form = SQLFORM.factory(
    17     18           Field("username", requires=IS_NOT_EMPTY()),
    18     19           Field("password", "password", requires=IS_NOT_EMPTY())
    19     20       )
    20     21       if login_form.accepts(request):
    21     22           results = newsblur.login(login_form.vars["username"], login_form.vars["password"])
    22         -        response.cookies["nb_cookie"] = newsblur.cookies
           23  +        response.cookies["nb_cookie"] = newsblur.cookies["newsblur_sessionid"]
    23     24           response.cookies["nb_cookie"]["path"] = "/"
    24     25           print "cookie =", newsblur.cookies
    25     26           redirect(URL("index"))
    26     27   
    27     28       return dict(login_form=login_form)

Modified applications/mobileblur/models/0_helpers.py from [6363f858c9] to [c67dbf684a].

     1      1   newsblur = local_import("newsblur")
            2  +newsblur = newsblur.NewsBlur()
     2      3   
     3      4   threshold = 0
     4      5   thresholds = ["nt", "ps", "ng"]  # indices -1, 0, 1 for negative, neutral, and positive intelligence filters
     5      6   
     6         -#import ipdb
     7         -#ipdb.set_trace()
     8         -if "nb_cookie" not in request.cookies.keys():
     9         -    if [request.application, request.controller, request.function] != [request.application, "default", "login"]:
            7  +print request.cookies
            8  +if [request.application, request.controller, request.function] != [request.application, "default", "login"]:
            9  +    if "nb_cookie" not in request.cookies.keys():
    10     10           redirect(URL("default", "login"))
    11         -#else:
    12         -#    newsblur.cookies = request.cookies["nb_cookie"]
           11  +    else:
           12  +        newsblur.cookies["newsblur_sessionid"] = request.cookies["nb_cookie"].value

Modified applications/mobileblur/modules/newsblur.py from [d2476742fe] to [f65190f3ac].

     1      1   #!/usr/bin/python
     2      2   
     3      3   """newsblur.py - An API wrapper library for newsblur.com"""
     4      4   
     5      5   import simplejson
     6         -
     7      6   import requests
     8      7   
     9      8   __author__ = 'Dananjaya Ramanayake <dananjaya86@gmail.com>, spiffytech <spiffytechgmail.com>'
    10      9   __version__ = "0.1"
    11     10   
    12     11   nb_url = "http://www.newsblur.com/"
    13         -cookies = None
    14         -
    15         -def login(username,password):
    16         -    '''
    17         -    Login as an existing user.
    18         -    If a user has no password set, you cannot just send any old password. 
    19         -    Required parameters, username and password, must be of string type.
    20         -    '''
    21         -
    22         -    url = nb_url + 'api/login'
    23         -    results = requests.post(url, data={"username": username, "password": password})
    24         -    global cookies
    25         -    cookies = results.cookies
    26         -    return simplejson.loads(results.content)
           12  +
           13  +class NewsBlur():
           14  +    def __init__(self):
           15  +        self.cookies = {}
           16  +
           17  +    def login(self, username,password):
           18  +        '''
           19  +        Login as an existing user.
           20  +        If a user has no password set, you cannot just send any old password. 
           21  +        Required parameters, username and password, must be of string type.
           22  +        '''
           23  +
           24  +        url = nb_url + 'api/login'
           25  +        results = requests.post(url, data={"username": username, "password": password})
           26  +        print "results.cookies =", results.cookies
           27  +        print type(results.cookies)
           28  +        self.cookies = results.cookies
           29  +        return simplejson.loads(results.content)
           30  +
           31  +    def logout(self, ):
           32  +        '''
           33  +        Logout the currently logged in user.
           34  +        '''
           35  +
           36  +        url = nb_url + 'api/logout'
           37  +        results = requests.get(url, cookies=self.cookies)
           38  +        return simplejson.loads(results.content)
           39  +
           40  +    def signup(self, username,password,email):
           41  +        '''
           42  +        Create a new user.
           43  +        All three required parameters must be of type string.
           44  +        '''
           45  +
           46  +        url = nb_url + 'api/signup'
           47  +        payload = {'signup_username':username,'signup_password':password,'signup_email':email}
           48  +        results = requests.post(url, data=payload, cookies=self.cookies)
           49  +        return simplejson.loads(results.content)
           50  +
           51  +    def search_feed(self, address,offset=1):
           52  +        '''
           53  +        
           54  +        Retrieve information about a feed from its website or RSS address.
           55  +        Parameter address must be of type string while parameter offset must be an integer.
           56  +        Will return a feed.
           57  +        
           58  +        '''
           59  +
           60  +        url = nb_url + 'rss_feeds/search_feed'
           61  +        payload = {'address':address,'offset':offset}
           62  +        results = results.get(url, data=payload, cookies=self.cookies)
           63  +        return simplejson.loads(results.content)
           64  +
           65  +    def feeds(self, include_favicons=True,flat=False):
           66  +        '''
           67  +        Retrieve a list of feeds to which a user is actively subscribed.
           68  +        Includes the 3 unread counts (positive, neutral, negative), as well as optional favicons.
           69  +        '''
           70  +        
           71  +        url = nb_url + 'reader/feeds'
           72  +        payload = {'include_favicons':include_favicons,'flat':flat}
           73  +        results = requests.get(url, data=payload, cookies=self.cookies)
           74  +        return simplejson.loads(results.content)
           75  +
           76  +
           77  +    def favicons(self, feeds=[1,2,3]):
           78  +        '''
           79  +        Retrieve a list of favicons for a list of feeds. 
           80  +        Used when combined with /reader/feeds and include_favicons=false, so the feeds request contains far less data. 
           81  +        Useful for mobile devices, but requires a second request. 
           82  +        '''
           83  +        
           84  +        url = nb_url + 'reader/favicons'
           85  +        payload = {'feeds':feeds}
           86  +        results = requests.get(url, data=payload, cookies=self.cookies)
           87  +        return simplejson.loads(results.content)
           88  +        
           89  +    def id(self, id_no):
           90  +        '''
           91  +        Retrieve the original page from a single feed.
           92  +        '''
           93  +        
           94  +        url = nb_url + 'reader/page/' % id_no
           95  +        payload = {}
           96  +        results = requests.get(url, data=payload, cookies=self.cookies)
           97  +        return simplejson.loads(results.content)
           98  +
           99  +    def refresh_feeds(self, ):
          100  +        '''
          101  +        Up-to-the-second unread counts for each active feed.
          102  +        Poll for these counts no more than once a minute.
          103  +        '''
          104  +
          105  +        url = nb_url + 'reader/refresh_feeds'
          106  +        results = requests.get(url, cookies=self.cookies)
          107  +        return simplejson.loads(results.content)
          108  +
          109  +    def feeds_trainer(self, feed_id):
          110  +        '''
          111  +        Retrieves all popular and known intelligence classifiers.
          112  +        Also includes user's own classifiers.
          113  +        '''
          114  +
          115  +        url = nb_url + 'reader/feeds_trainer'
          116  +        payload = {'feed_id':feed_id}
          117  +        results = requests.get(url, data=payload, cookies=self.cookies)
          118  +        return simplejson.loads(results.content)
          119  +
          120  +    def statistics(self, id_no):
          121  +        '''
          122  +        If you only want a user's classifiers, use /classifiers/:id.
          123  +        Omit the feed_id to get all classifiers for all subscriptions.
          124  +        '''
          125  +
          126  +        url = nb_url + 'rss_feeds/statistics/%d' % id_no
          127  +        results = requests.get(url, cookies=self.cookies)
          128  +        return simplejson.loads(results.content)
          129  +
          130  +    def feed_autocomplete(self, term):
          131  +        '''
          132  +        Get a list of feeds that contain a search phrase.
          133  +        Searches by feed address, feed url, and feed title, in that order.
          134  +        Will only show sites with 2+ subscribers.
          135  +        '''
          136  +
          137  +        url = nb_url + 'rss_feeds/feed_autocomplete?%'
          138  +        payload = {'term':term}
          139  +        results = requests.get(url, data=payload, cookies=self.cookies)
          140  +        return simplejson.loads(results.content)
          141  +
          142  +    def feed(self, id):
          143  +        '''
          144  +        Retrieve stories from a single feed.
          145  +        '''
          146  +
          147  +        url = nb_url + 'reader/feed/%s' % id
          148  +        results = requests.get(url, cookies=self.cookies)
          149  +        return simplejson.loads(results.content)
          150  +
          151  +    def starred_stories(self, page=1):
          152  +        '''
          153  +        Retrieve a user's starred stories.
          154  +        '''
          155  +        
          156  +        url = nb_url + 'reader/starred_stories'
          157  +        payload = {'page':page}
          158  +        results = requests.get(url, data=payload, cookies=self.cookies)
          159  +        return simplejson.loads(results.content)
          160  +
          161  +    def river_stories(self, feeds,page=1,read_stories_count=0):
          162  +        '''
          163  +        Retrieve stories from a collection of feeds. This is known as the River of News.
          164  +        Stories are ordered in reverse chronological order.
          165  +        '''
          166  +
          167  +        url = nb_url + 'reader/river_stories'
          168  +        payload = {'feeds':feeds,'page':page,'read_stories_count':read_stories_count}
          169  +        results = urllib2.urlopen(url, data=payload, cookies=self.cookies)
          170  +        return simplejson.loads(results.content)
          171  +
          172  +    def mark_story_as_read(self, story_id,feed_id):
          173  +        '''
          174  +        Mark stories as read.
          175  +        Multiple story ids can be sent at once.
          176  +        Each story must be from the same feed.
          177  +        '''
          178  +
          179  +        url = nb_url + 'reader/mark_story_as_read'
          180  +        payload = {'story_id':story_id,'feed_id':feed_id}
          181  +        results = requests.post(url, data=payload, cookies=self.cookies)
          182  +        return simplejson.loads(results.content)
          183  +
          184  +    def mark_story_as_starred(self, story_id,feed_id):
          185  +        '''
          186  +        Mark a story as starred (saved).
          187  +        '''
          188  +        
          189  +        url = nb_url + 'reader/mark_story_as_starred'
          190  +        payload = {'story_id':story_id,'feed_id':feed_id}
          191  +        results = requests.post(url, data=payload, cookies=self.cookies)
          192  +        return simplejson.loads(results.content)
          193  +
          194  +    def mark_all_as_read(self, days=0):
          195  +        '''
          196  +        Mark all stories in *all* feeds read.
          197  +        '''
          198  +        
          199  +        url = nb_url + 'reader/mark_all_as_read'
          200  +        payload = {'days':days}
          201  +        results = requests.post(url, data=payload, cookies=self.cookies)
          202  +        return simplejson.loads(results.content)
          203  +
          204  +    def add_url(self, url,folder='[Top Level]'):
          205  +        '''
          206  +        Add a feed by its URL. 
          207  +        Can be either the RSS feed or the website itself.
          208  +        '''
          209  +        
          210  +        url = nb_url + 'reader/add_url'
          211  +        payload = {'url':url,'folder':folder}
          212  +        results = requests.post(url, data=payload, cookies=self.cookies)
          213  +        return simplejson.loads(results.content)
          214  +
          215  +
          216  +    def add_folder(self, folder,parent_folder='[Top Level]'):
          217  +        '''
          218  +        Add a new folder.
          219  +        '''
          220  +        
          221  +        url = nb_url + 'reader/add_folder'
          222  +        payload = {'folder':folder,'parent_folder':parent_folder}
          223  +        results = requests.post(url, data=payload, cookies=self.cookies)
          224  +        return simplejson.loads(results.content)
          225  +
          226  +    def rename_feed(self, feed_title,feed_id):
          227  +        '''
          228  +        Rename a feed title. Only the current user will see the new title.
          229  +        '''
          230  +        
          231  +        url = nb_url + 'reader/rename_feed'
          232  +        payload = {'feed_title':feed_title,'feed_id':feed_id}
          233  +        results = requests.post(url, data=payload, cookies=self.cookies)
          234  +        return simplejson.loads(results.content)
          235  +
          236  +    def delete_feed(self, feed_id,in_folder):
          237  +        '''
          238  +        Unsubscribe from a feed. Removes it from the folder.
          239  +        Set the in_folder parameter to remove a feed from the correct folder, in case the user is subscribed to the feed in multiple folders.
          240  +        '''    
          241  +
          242  +        url = nb_url + 'reader/delete_feed'
          243  +        payload = {'feed_id':feed_id,'in_folder':in_folder}
          244  +        results = requests.post(url, data=payload, cookies=self.cookies)
          245  +        return simplejson.loads(results.content)
          246  +
          247  +    def rename_folder(self, folder_to_rename,new_folder_name,in_folder):
          248  +        '''
          249  +        Rename a folder.
          250  +        '''
          251  +        
          252  +        url = nb_url + 'reader/rename_folder'
          253  +        payload = {'folder_to_rename':folder_to_rename,'new_folder_name':new_folder_name,'in_folder':in_folder}
          254  +        results = requests.post(url, data=payload, cookies=self.cookies)
          255  +        return simplejson.loads(results.content)
          256  +
          257  +    def delete_folder(self, folder_to_delete,in_folder,feed_id):
          258  +        '''
          259  +        Delete a folder and unsubscribe from all feeds inside.
          260  +        '''
          261  +        
          262  +        url = nb_url + 'reader/delete_folder'
          263  +        payload = {'folder_to_delete':folder_to_delete,'in_folder':in_folder,'feed_id':feed_id}
          264  +        results = requests.post(url, data=payload, cookies=self.cookies)
          265  +        return simplejson.loads(results.content)
          266  +
          267  +
          268  +    def mark_feed_as_read(self, feed_id):
          269  +        '''
          270  +        Mark a list of feeds as read.
          271  +        '''
          272  +        
          273  +        url = nb_url + 'reader/mark_feed_as_read'
          274  +        payload = {'feed_id':feed_id}
          275  +        results = requests.post(url, data=payload, cookies=self.cookies)
          276  +        return simplejson.loads(results.content)
          277  +
          278  +
          279  +    def save_feed_order(self, folders):
          280  +        '''
          281  +        Reorder feeds and move them around between folders.
          282  +        The entire folder structure needs to be serialized.
          283  +        '''
          284  +
          285  +        url = nb_url + 'reader/save_feed_order'
          286  +        payload = {'folders':folders}
          287  +        results = requests.post(url, data=payload, cookies=self.cookies)
          288  +        return simplejson.loads(results.content)
          289  +
    27    290   
    28         -def logout():
    29         -    '''
    30         -    Logout the currently logged in user.
    31         -    '''
    32         -
    33         -    url = nb_url + 'api/logout'
    34         -    results = requests.get(url, cookies=cookies)
    35         -    return simplejson.loads(results.content)
    36         -
    37         -def signup(username,password,email):
    38         -    '''
    39         -    Create a new user.
    40         -    All three required parameters must be of type string.
    41         -    '''
          291  +    def classifier(self, id_no):
          292  +        '''
          293  +        Get the intelligence classifiers for a user's site.
          294  +        Only includes the user's own classifiers. 
          295  +        Use /reader/feeds_trainer for popular classifiers.
          296  +        '''
    42    297   
    43         -    url = nb_url + 'api/signup'
    44         -    payload = {'signup_username':username,'signup_password':password,'signup_email':email}
    45         -    results = requests.post(url, data=payload, cookies=cookies)
    46         -    return simplejson.loads(results.content)
    47         -
    48         -def search_feed(address,offset=1):
    49         -    '''
    50         -    
    51         -    Retrieve information about a feed from its website or RSS address.
    52         -    Parameter address must be of type string while parameter offset must be an integer.
    53         -    Will return a feed.
    54         -    
    55         -    '''
    56         -
    57         -    url = nb_url + 'rss_feeds/search_feed'
    58         -    payload = {'address':address,'offset':offset}
    59         -    results = results.get(url, data=payload, cookies=cookies)
    60         -    return simplejson.loads(results.content)
    61         -
    62         -def feeds(include_favicons=True,flat=False):
    63         -    '''
    64         -    Retrieve a list of feeds to which a user is actively subscribed.
    65         -    Includes the 3 unread counts (positive, neutral, negative), as well as optional favicons.
    66         -    '''
    67         -    
    68         -    url = nb_url + 'reader/feeds'
    69         -    payload = {'include_favicons':include_favicons,'flat':flat}
    70         -    results = requests.get(url, data=payload, cookies=cookies)
    71         -    return simplejson.loads(results.content)
          298  +        url = nb_url + 'classifier/%d' % id_no
          299  +        results = requests.get(url)
          300  +        return simplejson.loads(results.content)
    72    301   
    73    302   
    74         -def favicons(feeds=[1,2,3]):
    75         -    '''
    76         -    Retrieve a list of favicons for a list of feeds. 
    77         -    Used when combined with /reader/feeds and include_favicons=false, so the feeds request contains far less data. 
    78         -    Useful for mobile devices, but requires a second request. 
    79         -    '''
    80         -    
    81         -    url = nb_url + 'reader/favicons'
    82         -    payload = {'feeds':feeds}
    83         -    results = requests.get(url, data=payload, cookies=cookies)
    84         -    return simplejson.loads(results.content)
    85         -    
    86         -def id(id_no):
    87         -    '''
    88         -    Retrieve the original page from a single feed.
    89         -    '''
    90         -    
    91         -    url = nb_url + 'reader/page/' % id_no
    92         -    payload = {}
    93         -    results = requests.get(url, data=payload, cookies=cookies)
    94         -    return simplejson.loads(results.content)
    95         -
    96         -def refresh_feeds():
    97         -    '''
    98         -    Up-to-the-second unread counts for each active feed.
    99         -    Poll for these counts no more than once a minute.
   100         -    '''
   101         -
   102         -    url = nb_url + 'reader/refresh_feeds'
   103         -    results = requests.get(url, cookies=cookies)
   104         -    return simplejson.loads(results.content)
   105         -
   106         -def feeds_trainer(feed_id):
   107         -    '''
   108         -    Retrieves all popular and known intelligence classifiers.
   109         -    Also includes user's own classifiers.
   110         -    '''
   111         -
   112         -    url = nb_url + 'reader/feeds_trainer'
   113         -    payload = {'feed_id':feed_id}
   114         -    results = requests.get(url, data=payload, cookies=cookies)
   115         -    return simplejson.loads(results.content)
   116         -
   117         -def statistics(id_no):
   118         -    '''
   119         -    If you only want a user's classifiers, use /classifiers/:id.
   120         -    Omit the feed_id to get all classifiers for all subscriptions.
   121         -    '''
   122         -
   123         -    url = nb_url + 'rss_feeds/statistics/%d' % id_no
   124         -    results = requests.get(url, cookies=cookies)
   125         -    return simplejson.loads(results.content)
   126         -
   127         -def feed_autocomplete(term):
   128         -    '''
   129         -    Get a list of feeds that contain a search phrase.
   130         -    Searches by feed address, feed url, and feed title, in that order.
   131         -    Will only show sites with 2+ subscribers.
   132         -    '''
   133         -
   134         -    url = nb_url + 'rss_feeds/feed_autocomplete?%'
   135         -    payload = {'term':term}
   136         -    results = requests.get(url, data=payload, cookies=cookies)
   137         -    return simplejson.loads(results.content)
   138         -
   139         -def feed(id):
   140         -    '''
   141         -    Retrieve stories from a single feed.
   142         -    '''
   143         -
   144         -    url = nb_url + 'reader/feed/%s' % id
   145         -    results = requests.get(url, cookies=cookies)
   146         -    return simplejson.loads(results.content)
   147         -
   148         -def starred_stories(page=1):
   149         -    '''
   150         -    Retrieve a user's starred stories.
   151         -    '''
   152         -    
   153         -    url = nb_url + 'reader/starred_stories'
   154         -    payload = {'page':page}
   155         -    results = requests.get(url, data=payload, cookies=cookies)
   156         -    return simplejson.loads(results.content)
   157         -
   158         -def river_stories(feeds,page=1,read_stories_count=0):
   159         -    '''
   160         -    Retrieve stories from a collection of feeds. This is known as the River of News.
   161         -    Stories are ordered in reverse chronological order.
   162         -    '''
   163         -
   164         -    url = nb_url + 'reader/river_stories'
   165         -    payload = {'feeds':feeds,'page':page,'read_stories_count':read_stories_count}
   166         -    results = urllib2.urlopen(url, data=payload, cookies=cookies)
   167         -    return simplejson.loads(results.content)
   168         -
   169         -def mark_story_as_read(story_id,feed_id):
   170         -    '''
   171         -    Mark stories as read.
   172         -    Multiple story ids can be sent at once.
   173         -    Each story must be from the same feed.
   174         -    '''
   175         -
   176         -    url = nb_url + 'reader/mark_story_as_read'
   177         -    payload = {'story_id':story_id,'feed_id':feed_id}
   178         -    results = requests.post(url, data=payload, cookies=cookies)
   179         -    return simplejson.loads(results.content)
   180         -
   181         -def mark_story_as_starred(story_id,feed_id):
   182         -    '''
   183         -    Mark a story as starred (saved).
   184         -    '''
   185         -    
   186         -    url = nb_url + 'reader/mark_story_as_starred'
   187         -    payload = {'story_id':story_id,'feed_id':feed_id}
   188         -    results = requests.post(url, data=payload, cookies=cookies)
   189         -    return simplejson.loads(results.content)
   190         -
   191         -def mark_all_as_read(days=0):
   192         -    '''
   193         -    Mark all stories in *all* feeds read.
   194         -    '''
   195         -    
   196         -    url = nb_url + 'reader/mark_all_as_read'
   197         -    payload = {'days':days}
   198         -    results = requests.post(url, data=payload, cookies=cookies)
   199         -    return simplejson.loads(results.content)
   200         -
   201         -def add_url(url,folder='[Top Level]'):
   202         -    '''
   203         -    Add a feed by its URL. 
   204         -    Can be either the RSS feed or the website itself.
   205         -    '''
   206         -    
   207         -    url = nb_url + 'reader/add_url'
   208         -    payload = {'url':url,'folder':folder}
   209         -    results = requests.post(url, data=payload, cookies=cookies)
   210         -    return simplejson.loads(results.content)
          303  +    def classifier_save(self, like_type,dislike_type,remove_like_type,remove_dislike_type):
          304  +        '''
          305  +        Save intelligence classifiers (tags, titles, authors, and the feed) for a feed.
          306  +        '''
          307  +        
          308  +        url = nb_url + 'classifier/save'
          309  +        payload = {'like_[TYPE]':like_type,
          310  +                       'dislike_[TYPE]':dislike_type,
          311  +                        'remove_like_[TYPE]':remove_like_type,
          312  +                       'remove_dislike_[TYPE]':remove_dislike_type}
          313  +        results = requests.post(url, data=payload, cookies=self.cookies)
          314  +        return simplejson.loads(results.content)
   211    315   
   212    316   
   213         -def add_folder(folder,parent_folder='[Top Level]'):
   214         -    '''
   215         -    Add a new folder.
   216         -    '''
   217         -    
   218         -    url = nb_url + 'reader/add_folder'
   219         -    payload = {'folder':folder,'parent_folder':parent_folder}
   220         -    results = requests.post(url, data=payload, cookies=cookies)
   221         -    return simplejson.loads(results.content)
   222         -
   223         -def rename_feed(feed_title,feed_id):
   224         -    '''
   225         -    Rename a feed title. Only the current user will see the new title.
   226         -    '''
   227         -    
   228         -    url = nb_url + 'reader/rename_feed'
   229         -    payload = {'feed_title':feed_title,'feed_id':feed_id}
   230         -    results = requests.post(url, data=payload, cookies=cookies)
   231         -    return simplejson.loads(results.content)
   232         -
   233         -def delete_feed(feed_id,in_folder):
   234         -    '''
   235         -    Unsubscribe from a feed. Removes it from the folder.
   236         -    Set the in_folder parameter to remove a feed from the correct folder, in case the user is subscribed to the feed in multiple folders.
   237         -    '''    
   238         -
   239         -    url = nb_url + 'reader/delete_feed'
   240         -    payload = {'feed_id':feed_id,'in_folder':in_folder}
   241         -    results = requests.post(url, data=payload, cookies=cookies)
   242         -    return simplejson.loads(results.content)
   243         -
   244         -def rename_folder(folder_to_rename,new_folder_name,in_folder):
   245         -    '''
   246         -    Rename a folder.
   247         -    '''
   248         -    
   249         -    url = nb_url + 'reader/rename_folder'
   250         -    payload = {'folder_to_rename':folder_to_rename,'new_folder_name':new_folder_name,'in_folder':in_folder}
   251         -    results = requests.post(url, data=payload, cookies=cookies)
   252         -    return simplejson.loads(results.content)
   253         -
   254         -def delete_folder(folder_to_delete,in_folder,feed_id):
   255         -    '''
   256         -    Delete a folder and unsubscribe from all feeds inside.
   257         -    '''
   258         -    
   259         -    url = nb_url + 'reader/delete_folder'
   260         -    payload = {'folder_to_delete':folder_to_delete,'in_folder':in_folder,'feed_id':feed_id}
   261         -    results = requests.post(url, data=payload, cookies=cookies)
   262         -    return simplejson.loads(results.content)
   263         -
   264         -
   265         -def mark_feed_as_read(feed_id):
   266         -    '''
   267         -    Mark a list of feeds as read.
   268         -    '''
   269         -    
   270         -    url = nb_url + 'reader/mark_feed_as_read'
   271         -    payload = {'feed_id':feed_id}
   272         -    results = requests.post(url, data=payload, cookies=cookies)
   273         -    return simplejson.loads(results.content)
   274         -
   275         -
   276         -def save_feed_order(folders):
   277         -    '''
   278         -    Reorder feeds and move them around between folders.
   279         -    The entire folder structure needs to be serialized.
   280         -    '''
   281         -
   282         -    url = nb_url + 'reader/save_feed_order'
   283         -    payload = {'folders':folders}
   284         -    results = requests.post(url, data=payload, cookies=cookies)
   285         -    return simplejson.loads(results.content)
   286         -
   287         -
   288         -def classifier(id_no):
   289         -    '''
   290         -    Get the intelligence classifiers for a user's site.
   291         -    Only includes the user's own classifiers. 
   292         -    Use /reader/feeds_trainer for popular classifiers.
   293         -    '''
   294         -
   295         -    url = nb_url + 'classifier/%d' % id_no
   296         -    results = requests.get(url)
   297         -    return simplejson.loads(results.content)
   298         -
   299         -
   300         -def classifier_save(like_type,dislike_type,remove_like_type,remove_dislike_type):
   301         -    '''
   302         -    Save intelligence classifiers (tags, titles, authors, and the feed) for a feed.
   303         -    '''
   304         -    
   305         -    url = nb_url + 'classifier/save'
   306         -    payload = {'like_[TYPE]':like_type,
   307         -                   'dislike_[TYPE]':dislike_type,
   308         -                    'remove_like_[TYPE]':remove_like_type,
   309         -                   'remove_dislike_[TYPE]':remove_dislike_type}
   310         -    results = requests.post(url, data=payload, cookies=cookies)
   311         -    return simplejson.loads(results.content)
   312         -
   313         -
   314         -def opml_export():
   315         -    '''
   316         -    Download a backup of feeds and folders as an OPML file.
   317         -    Contains folders and feeds in XML; useful for importing in another RSS reader.
   318         -    '''
   319         -    
   320         -    url = nb_url + 'import/opml_export'
   321         -    results = requests.get(url)
   322         -    return simplejson.loads(results.content)
          317  +    def opml_export(self, ):
          318  +        '''
          319  +        Download a backup of feeds and folders as an OPML file.
          320  +        Contains folders and feeds in XML; useful for importing in another RSS reader.
          321  +        '''
          322  +        
          323  +        url = nb_url + 'import/opml_export'
          324  +        results = requests.get(url)
          325  +        return simplejson.loads(results.content)
   323    326   
   324    327   
   325    328   
   326         -def opml_upload(opml_file):
   327         -    '''
   328         -    Upload an OPML file.
   329         -    '''
   330         -    
   331         -    url = nb_url + 'import/opml_upload'
   332         -    f = open(opml_file)
   333         -    payload = {'file':f}
   334         -    f.close()
   335         -    results = requests.post(url, data=payload, cookies=cookies)
   336         -    return simplejson.loads(results.content)
          329  +    def opml_upload(self, opml_file):
          330  +        '''
          331  +        Upload an OPML file.
          332  +        '''
          333  +        
          334  +        url = nb_url + 'import/opml_upload'
          335  +        f = open(opml_file)
          336  +        payload = {'file':f}
          337  +        f.close()
          338  +        results = requests.post(url, data=payload, cookies=self.cookies)
          339  +        return simplejson.loads(results.content)

Modified gluon/__init__.pyc from [4b1dda92da] to [5cc6bba7a9].

cannot compute difference between binary files

Modified gluon/admin.pyc from [f384b2f33c] to [79ffa4f52b].

cannot compute difference between binary files

Modified gluon/cache.pyc from [4c62f4a468] to [ee2fd24717].

cannot compute difference between binary files

Modified gluon/cfs.pyc from [c27431a52a] to [53702fcc9d].

cannot compute difference between binary files

Modified gluon/compileapp.pyc from [fde14323b3] to [dcbe76af54].

cannot compute difference between binary files

Modified gluon/contenttype.pyc from [e29ed3a249] to [c1d12f37e3].

cannot compute difference between binary files

Modified gluon/custom_import.pyc from [df735202af] to [00151440cf].

cannot compute difference between binary files

Modified gluon/dal.pyc from [20a5258c74] to [ef3f83ebfe].

cannot compute difference between binary files

Modified gluon/decoder.pyc from [7da62cc626] to [35a33e2b2b].

cannot compute difference between binary files

Modified gluon/fileutils.pyc from [f01ef6af2e] to [01211dcf79].

cannot compute difference between binary files

Modified gluon/globals.pyc from [3c7f502a11] to [04ca4032e7].

cannot compute difference between binary files

Modified gluon/highlight.pyc from [7e17a5f9fa] to [5942d409ce].

cannot compute difference between binary files

Modified gluon/html.pyc from [8f42eff40f] to [824bd62037].

cannot compute difference between binary files

Modified gluon/http.pyc from [c8d52a9b33] to [534d9c72d6].

cannot compute difference between binary files

Modified gluon/languages.pyc from [cc4448c9de] to [0996aa7396].

cannot compute difference between binary files

Modified gluon/main.pyc from [7db36fceca] to [7ddd622055].

cannot compute difference between binary files

Modified gluon/myregex.pyc from [e888c61100] to [d69e0ed8e9].

cannot compute difference between binary files

Modified gluon/newcron.pyc from [ad7a061b21] to [0679b97e21].

cannot compute difference between binary files

Modified gluon/portalocker.pyc from [9f3bbb0a55] to [8635463535].

cannot compute difference between binary files

Modified gluon/restricted.pyc from [52584fef22] to [2508bf3173].

cannot compute difference between binary files

Modified gluon/rewrite.pyc from [1f6cf594b7] to [b08a9d9aa8].

cannot compute difference between binary files

Modified gluon/rocket.pyc from [30fb9fab51] to [97b6551760].

cannot compute difference between binary files

Modified gluon/sanitizer.pyc from [d789c7453f] to [b32ea719f3].

cannot compute difference between binary files

Modified gluon/serializers.pyc from [50c93bfb9c] to [96f33f4817].

cannot compute difference between binary files

Modified gluon/settings.pyc from [ee34a24607] to [e337445a3a].

cannot compute difference between binary files

Modified gluon/shell.pyc from [cb888418ec] to [28e6a5f56e].

cannot compute difference between binary files

Modified gluon/sqlhtml.pyc from [b9c43229a5] to [12f6acce05].

cannot compute difference between binary files

Modified gluon/storage.pyc from [a1d5b27c69] to [5ded006a69].

cannot compute difference between binary files

Modified gluon/streamer.pyc from [59d659d0b5] to [2a88153c65].

cannot compute difference between binary files

Modified gluon/template.pyc from [892f7ab761] to [461edee807].

cannot compute difference between binary files

Modified gluon/tools.pyc from [3a8feefb6f] to [366fc7ea69].

cannot compute difference between binary files

Modified gluon/utils.pyc from [59bcf5d046] to [16b3176237].

cannot compute difference between binary files

Modified gluon/validators.pyc from [4ee16a78b6] to [16128513c3].

cannot compute difference between binary files

Modified gluon/widget.pyc from [5fe6811bf3] to [4cc96f8ee2].

cannot compute difference between binary files

Modified gluon/xmlrpc.pyc from [6e66be7d0b] to [1ea37d7148].

cannot compute difference between binary files