Data fetching from server now done in separate thread.
[canola-rtm] / canola-rtm / rtm / client.py
1 # -*- coding: utf-8 -*-
2 # Canola2 Remember The Milk Plugin
3 # Authors: Andrey Popelo <andrey@popelo.com>
4 #
5 # Based on Python module for Remember The Milk API
6 # (http://intellectronica.net/python-rtm) 
7 # originally created by Tom Berger <tom.berger@gmail.com>
8 #
9 # This program is free software: you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation, either version 3 of the License, or
12 # (at your option) any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 #
22 # Additional permission under GNU GPL version 3 section 7
23 #
24 # If you modify this Program, or any covered work, by linking or combining it
25 # with Canola2 and its core components (or a modified version of any of those),
26 # containing parts covered by the terms of Instituto Nokia de Tecnologia End
27 # User Software Agreement, the licensors of this Program grant you additional
28 # permission to convey the resulting work.
29
30 from urllib import urlencode
31 from urllib2 import urlopen
32 from md5 import md5
33
34
35 class Method(object):
36     """Remember The Milk API method.
37     
38     This class represents an RTM API method. Together with Client class and
39     Category class it allows to access RTM API methods using dot-notation.
40     Eg.
41                 rtm.tasks.getList()
42        Client --^   ^     ^-- Method
43                     └- Category
44     """
45
46     def __init__(self, client, category_name, method_name):
47         self.client = client
48         self.category_name = category_name
49         self.method_name = method_name
50
51     def __call__(self, **kwargs):
52         return self.client.get(
53             method='rtm.%s.%s' % (self.category_name, self.method_name),
54             auth_token=self.client.token,
55             **kwargs)
56
57
58 class Category(object):
59     """Remember The Milk API category.
60     
61     This class represents an RTM API category. Together with Client class and
62     Method class it allows to access RTM API methods using dot-notation.
63     Eg.
64                 rtm.tasks.getList()
65        Client --^   ^     ^-- Method
66                     └- Category
67     """
68
69     def __init__(self, client, category_name):
70         self.client = client
71         self.category_name = category_name
72
73     def __getattr__(self, attr):
74         return Method(self.client, self.category_name, attr)
75
76
77 class Client(object):
78     """Remember The Milk Backend.
79
80     This class provides an interface to manage data on Remember The Milk
81     server.
82     You can access RTM API methods using dot-notation.
83     Eg.
84                 rtm.tasks.getList()
85        Client --^   ^     ^-- Method
86                     └- Category
87     """
88
89     URL_SERVICE_REST = "http://api.rememberthemilk.com/services/rest/"
90     URL_SERVICE_AUTH = "http://www.rememberthemilk.com/services/auth/"
91
92     def __init__(self, api_key, secret, token=None):
93         self.api_key = api_key
94         self.secret = secret
95         self._token = token
96         self._frob = None
97
98     def __getattr__(self, attr):
99         return Category(self, attr)
100
101     def sign(self, params):
102         data = self.secret + ''.join(
103             k + params[k] for k in sorted(params.keys()))
104
105         # KILLME
106         print "\n Sign data: " + data + "\n"
107
108         return md5(data).hexdigest()
109
110     def fetch(self, url, **kwargs):
111         if kwargs:
112             url = url + '?' + urlencode(kwargs)
113
114         # KILLME
115         print "\nRTM Request:\n  " + url + "\n"
116
117         return urlopen(url).read()
118
119     def get(self, **params):
120         params['api_key'] = self.api_key
121         params['format'] = 'json'
122         params['api_sig'] = self.sign(params)
123
124         json = self.fetch(self.URL_SERVICE_REST, **params)
125
126         # KILLME
127         print "\nRTM Response:\n  " + json + "\n"
128
129         # TODO parse json and return valid data
130
131         return None
132
133     @property
134     def auth_url(self):
135         params = {
136             'api_key': self.api_key,
137             'perms'  : 'delete',
138             'frob'   : self.frob
139             }
140         params['api_sig'] = self.sign(params)
141         return self.URL_SERVICE_AUTH + '?' + urlencode(params)
142
143     @property
144     def token(self):
145         if self._token is None:
146             frob = self._frob
147             rsp = self.get(method='rtm.auth.getToken', frob=frob)
148             self._token = rsp.auth.token
149         return self._token
150
151     @property
152     def frob(self):
153         if self._frob is None:
154             rsp = self.get(method='rtm.auth.getFrob')
155             self._frob = rsp.frob
156         return self._frob