MobileBlur

Check-in [2ac904a27b]
Login
Overview
Comment:Added support for multiple users to use the site simultaneously
Timelines: family | ancestors | descendants | both | trunk | v0.2
Files: files | file ages | folders
SHA1: 2ac904a27b75073b12976cdf799a20e053200c4d
User & Date: spiffy on 2011-11-20 21:41:52
Other Links: manifest | tags
Context
2011-11-30
04:12
Made the app substantially prettier check-in: 517f4aa073 user: spiffy tags: trunk
2011-11-20
21:41
Added support for multiple users to use the site simultaneously check-in: 2ac904a27b user: spiffy tags: trunk, v0.2
21:04
Added a catch to handle a failed Newsblur login check-in: 759f07e810 user: spiffy tags: develop
18:36
Release 0.1- support for a single user, basic functionality works check-in: 56de658fd0 user: spiffy tags: trunk, v0.1
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Modified applications/mobileblur/controllers/default.py from [b38b4efcae] to [4edf6967cb].

     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:
................................................................................
    14     15   
    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         -        results = newsblur.login(login_form.vars["username"], login_form.vars["password"])
    22         -        response.cookies["nb_cookie"] = newsblur.cookies
    23         -        response.cookies["nb_cookie"]["path"] = "/"
    24         -        print "cookie =", newsblur.cookies
    25         -        redirect(URL("index"))
           22  +        try:
           23  +            results = newsblur.login(login_form.vars["username"], login_form.vars["password"])
           24  +            response.cookies["nb_cookie"] = newsblur.cookies["newsblur_sessionid"]
           25  +            response.cookies["nb_cookie"]["path"] = "/"
           26  +            print "cookie =", newsblur.cookies
           27  +            redirect(URL("index"))
           28  +        except Exception as ex:
           29  +            login_form.insert(-1, ex.message)
    26     30   
    27     31       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 [a487a766ef].

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