Merge lp:~mvo/software-center/recommends into lp:software-center
- recommends
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 2638 |
Proposed branch: | lp:~mvo/software-center/recommends |
Merge into: | lp:software-center |
Diff against target: |
1434 lines (+590/-507) 22 files modified
debian/control (+0/-1) softwarecenter/backend/login.py (+0/-7) softwarecenter/backend/oneconfhandler/core.py (+5/-11) softwarecenter/backend/piston/sreclient_pristine.py (+41/-0) softwarecenter/backend/piston/sso_helper.py (+85/-0) softwarecenter/backend/piston/ubuntusso_pristine.py (+22/-0) softwarecenter/backend/recommends.py (+82/-0) softwarecenter/backend/reviews/__init__.py (+3/-5) softwarecenter/backend/reviews/rnr.py (+11/-13) softwarecenter/backend/scagent.py (+32/-27) softwarecenter/backend/spawn_helper.py (+24/-0) softwarecenter/backend/ubuntusso.py (+46/-249) softwarecenter/enums.py (+9/-0) softwarecenter/paths.py (+1/-3) test/gtk3/test_views.py (+2/-0) test/test_reviews.py (+3/-2) test/test_ubuntu_sso_api.py (+4/-4) utils/piston-helpers/piston_generic_helper.py (+215/-0) utils/piston-helpers/piston_get_review_stats_helper.py (+0/-65) utils/piston-helpers/piston_get_scagent_available_apps.py.THIS (+2/-57) utils/piston-helpers/piston_get_useful_votes_helper.py (+0/-46) utils/submit_review_gtk3.py (+3/-17) |
To merge this branch: | bzr merge lp:~mvo/software-center/recommends |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
software-store-developers | Pending | ||
Review via email: mp+87639@code.launchpad.net |
Commit message
Description of the change
This should be merged after the lp:~mvo/software-center/replace-restfulclient-with-piston branch gets merged.
Its adds a basic GObject for the recommender server, nothing more yet.
Gary Lasker (gary-lasker) wrote : | # |
- 2630. By Michael Vogt
-
add "recommend_me" call
Michael Vogt (mvo) wrote : | # |
On Fri, Jan 06, 2012 at 06:18:29AM -0000, Gary Lasker wrote:
> Hey Michael! Please double check one thing, there's a funky rename in the last rev of the branch:
>
> utils/piston-
>
> There are also a few simple pyflakes fixes that we'll want to do before merging.
Thanks! I merged your branch that cleans this up now and moved it into
trunk.
> This is a very nice kickoff for the client-side recommendations work, thanks!!
>
Great! Happy that you like it. Now we just need to add a bit of up :)
Cheers,
Michael
Preview Diff
1 | === added symlink 'data/piston_generic_helper.py' | |||
2 | === target is u'../utils/piston-helpers/piston_generic_helper.py' | |||
3 | === added symlink 'data/piston_get_recommends.py' | |||
4 | === target is u'../utils/piston-helpers/piston_get_recommends.py' | |||
5 | === removed symlink 'data/piston_get_review_stats_helper.py' | |||
6 | === target was u'../utils/piston-helpers/piston_get_review_stats_helper.py' | |||
7 | === removed symlink 'data/piston_get_scagent_available_apps.py' | |||
8 | === target was u'../utils/piston-helpers/piston_get_scagent_available_apps.py' | |||
9 | === removed symlink 'data/piston_get_useful_votes_helper.py' | |||
10 | === target was u'../utils/piston-helpers/piston_get_useful_votes_helper.py' | |||
11 | === modified file 'debian/control' | |||
12 | --- debian/control 2011-12-06 14:22:20 +0000 | |||
13 | +++ debian/control 2012-01-05 15:35:32 +0000 | |||
14 | @@ -40,7 +40,6 @@ | |||
15 | 40 | policykit-1, | 40 | policykit-1, |
16 | 41 | policykit-1-gnome | policykit-1-kde, | 41 | policykit-1-gnome | policykit-1-kde, |
17 | 42 | python-xdg, | 42 | python-xdg, |
18 | 43 | python-lazr.restfulclient, | ||
19 | 44 | ubuntu-sso-client (>= 0.99.6), | 43 | ubuntu-sso-client (>= 0.99.6), |
20 | 45 | python-piston-mini-client (>= 0.1+bzr29), | 44 | python-piston-mini-client (>= 0.1+bzr29), |
21 | 46 | oneconf (>= 0.2.6), | 45 | oneconf (>= 0.2.6), |
22 | 47 | 46 | ||
23 | === modified file 'softwarecenter/backend/login.py' | |||
24 | --- softwarecenter/backend/login.py 2011-09-15 14:24:14 +0000 | |||
25 | +++ softwarecenter/backend/login.py 2012-01-05 15:35:32 +0000 | |||
26 | @@ -49,10 +49,3 @@ | |||
27 | 49 | raise NotImplemented | 49 | raise NotImplemented |
28 | 50 | def cancel_login(self): | 50 | def cancel_login(self): |
29 | 51 | self.emit("login-canceled") | 51 | self.emit("login-canceled") |
30 | 52 | @property | ||
31 | 53 | def new_account_url(self): | ||
32 | 54 | return self.NEW_ACCOUNT_URL | ||
33 | 55 | @property | ||
34 | 56 | def forgoten_password_url(self): | ||
35 | 57 | return self.FORGOT_PASSWORD_URL | ||
36 | 58 | |||
37 | 59 | 52 | ||
38 | === modified file 'softwarecenter/backend/oneconfhandler/core.py' | |||
39 | --- softwarecenter/backend/oneconfhandler/core.py 2011-10-26 12:01:19 +0000 | |||
40 | +++ softwarecenter/backend/oneconfhandler/core.py 2012-01-05 15:35:32 +0000 | |||
41 | @@ -22,7 +22,7 @@ | |||
42 | 22 | from oneconf.enums import MIN_TIME_WITHOUT_ACTIVITY | 22 | from oneconf.enums import MIN_TIME_WITHOUT_ACTIVITY |
43 | 23 | 23 | ||
44 | 24 | from softwarecenter.backend.login_sso import get_sso_backend | 24 | from softwarecenter.backend.login_sso import get_sso_backend |
46 | 25 | from softwarecenter.backend.restfulclient import get_ubuntu_sso_backend | 25 | from softwarecenter.backend.ubuntusso import get_ubuntu_sso_backend |
47 | 26 | from softwarecenter.utils import clear_token_from_ubuntu_sso | 26 | from softwarecenter.utils import clear_token_from_ubuntu_sso |
48 | 27 | 27 | ||
49 | 28 | import datetime | 28 | import datetime |
50 | @@ -176,6 +176,8 @@ | |||
51 | 176 | self.ssoapi = get_ubuntu_sso_backend(token) | 176 | self.ssoapi = get_ubuntu_sso_backend(token) |
52 | 177 | self.ssoapi.connect("whoami", self._whoami_done) | 177 | self.ssoapi.connect("whoami", self._whoami_done) |
53 | 178 | self.ssoapi.connect("error", self._whoami_error) | 178 | self.ssoapi.connect("error", self._whoami_error) |
54 | 179 | # this will automatically verify the token and retrigger login | ||
55 | 180 | # if its expired | ||
56 | 179 | self.ssoapi.whoami() | 181 | self.ssoapi.whoami() |
57 | 180 | 182 | ||
58 | 181 | def _whoami_done(self, ssologin, result): | 183 | def _whoami_done(self, ssologin, result): |
59 | @@ -184,13 +186,5 @@ | |||
60 | 184 | 186 | ||
61 | 185 | def _whoami_error(self, ssologin, e): | 187 | def _whoami_error(self, ssologin, e): |
62 | 186 | logging.error("whoami error '%s'" % e) | 188 | logging.error("whoami error '%s'" % e) |
73 | 187 | # HACK: clear the token from the keyring assuming that it expired | 189 | self._share_inventory(False) |
74 | 188 | # or got deauthorized by the user on the website | 190 | return |
65 | 189 | # this really should be done by ubuntu-sso-client itself | ||
66 | 190 | import lazr.restfulclient.errors | ||
67 | 191 | errortype = lazr.restfulclient.errors.HTTPError | ||
68 | 192 | if (type(e) == errortype): | ||
69 | 193 | LOG.warn("authentication error, resetting token and retrying") | ||
70 | 194 | clear_token_from_ubuntu_sso(self.appname) | ||
71 | 195 | self._share_inventory(False) | ||
72 | 196 | return | ||
75 | 197 | 191 | ||
76 | === added file 'softwarecenter/backend/piston/sreclient_pristine.py' | |||
77 | --- softwarecenter/backend/piston/sreclient_pristine.py 1970-01-01 00:00:00 +0000 | |||
78 | +++ softwarecenter/backend/piston/sreclient_pristine.py 2012-01-05 15:35:32 +0000 | |||
79 | @@ -0,0 +1,41 @@ | |||
80 | 1 | from piston_mini_client import (PistonAPI, PistonResponseObject, | ||
81 | 2 | returns_list_of, returns_json) | ||
82 | 3 | from piston_mini_client.validators import (validate_pattern, validate, | ||
83 | 4 | oauth_protected) | ||
84 | 5 | |||
85 | 6 | # These are factored out as constants for if you need to work against a | ||
86 | 7 | # server that doesn't support both schemes (like http-only dev servers) | ||
87 | 8 | PUBLIC_API_SCHEME = 'http' | ||
88 | 9 | AUTHENTICATED_API_SCHEME = 'https' | ||
89 | 10 | |||
90 | 11 | class SoftwareCenterRecommenderAPI(PistonAPI): | ||
91 | 12 | default_service_root = 'http://localhost:8000/api/2.0' | ||
92 | 13 | |||
93 | 14 | @returns_json | ||
94 | 15 | def server_status(self): | ||
95 | 16 | return self._get('server-status', scheme=PUBLIC_API_SCHEME) | ||
96 | 17 | |||
97 | 18 | @oauth_protected | ||
98 | 19 | @returns_json | ||
99 | 20 | def profile(self): | ||
100 | 21 | return self._get('profile', scheme=PUBLIC_API_SCHEME) | ||
101 | 22 | |||
102 | 23 | @oauth_protected | ||
103 | 24 | def recommend_me(self): | ||
104 | 25 | return self._get('recommended_me', scheme=PUBLIC_API_SCHEME) | ||
105 | 26 | |||
106 | 27 | @oauth_protected | ||
107 | 28 | @validate('pkgname', str) | ||
108 | 29 | def recommend_app(self, pkgname): | ||
109 | 30 | return self._get('recommended_app/%s/' % pkgname, | ||
110 | 31 | scheme=PUBLIC_API_SCHEME) | ||
111 | 32 | |||
112 | 33 | @returns_json | ||
113 | 34 | def recommend_top(self): | ||
114 | 35 | return self._get('recommend_top', scheme=PUBLIC_API_SCHEME) | ||
115 | 36 | |||
116 | 37 | @returns_json | ||
117 | 38 | def feedback(self): | ||
118 | 39 | return self._get('feedback', scheme=PUBLIC_API_SCHEME) | ||
119 | 40 | |||
120 | 41 | |||
121 | 0 | 42 | ||
122 | === added file 'softwarecenter/backend/piston/sso_helper.py' | |||
123 | --- softwarecenter/backend/piston/sso_helper.py 1970-01-01 00:00:00 +0000 | |||
124 | +++ softwarecenter/backend/piston/sso_helper.py 2012-01-05 15:35:32 +0000 | |||
125 | @@ -0,0 +1,85 @@ | |||
126 | 1 | #!/usr/bin/python | ||
127 | 2 | # Copyright (C) 2012 Canonical | ||
128 | 3 | # | ||
129 | 4 | # Authors: | ||
130 | 5 | # Michael Vogt | ||
131 | 6 | # | ||
132 | 7 | # This program is free software; you can redistribute it and/or modify it under | ||
133 | 8 | # the terms of the GNU General Public License as published by the Free Software | ||
134 | 9 | # Foundation; version 3. | ||
135 | 10 | # | ||
136 | 11 | # This program is distributed in the hope that it will be useful, but WITHOUT | ||
137 | 12 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
138 | 13 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
139 | 14 | # details. | ||
140 | 15 | # | ||
141 | 16 | # You should have received a copy of the GNU General Public License along with | ||
142 | 17 | # this program; if not, write to the Free Software Foundation, Inc., | ||
143 | 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
144 | 19 | |||
145 | 20 | from gi.repository import GObject | ||
146 | 21 | from gettext import gettext as _ | ||
147 | 22 | |||
148 | 23 | from softwarecenter.backend.login_sso import get_sso_backend | ||
149 | 24 | from softwarecenter.backend.restfulclient import UbuntuSSOAPI | ||
150 | 25 | from softwarecenter.enums import (SOFTWARE_CENTER_NAME_KEYRING, | ||
151 | 26 | SOFTWARE_CENTER_SSO_DESCRIPTION, | ||
152 | 27 | ) | ||
153 | 28 | from softwarecenter.utils import clear_token_from_ubuntu_sso | ||
154 | 29 | |||
155 | 30 | |||
156 | 31 | |||
157 | 32 | class SSOLoginHelper(object): | ||
158 | 33 | def __init__(self, xid=0): | ||
159 | 34 | self.oauth = None | ||
160 | 35 | self.xid = xid | ||
161 | 36 | self.loop = GObject.MainLoop(GObject.main_context_default()) | ||
162 | 37 | |||
163 | 38 | def _login_successful(self, sso_backend, oauth_result): | ||
164 | 39 | self.oauth = oauth_result | ||
165 | 40 | # FIXME: actually verify the token against ubuntu SSO | ||
166 | 41 | self.loop.quit() | ||
167 | 42 | |||
168 | 43 | def verify_token(self, token): | ||
169 | 44 | def _whoami_done(sso, me): | ||
170 | 45 | self._whoami = me | ||
171 | 46 | self.loop.quit() | ||
172 | 47 | def _whoami_error(sso, err): | ||
173 | 48 | #print "ERRR", err | ||
174 | 49 | self.loop.quit() | ||
175 | 50 | self._whoami = None | ||
176 | 51 | sso = UbuntuSSOAPI(token) | ||
177 | 52 | sso.connect("whoami", _whoami_done) | ||
178 | 53 | sso.connect("error", _whoami_error) | ||
179 | 54 | sso.whoami() | ||
180 | 55 | self.loop.run() | ||
181 | 56 | # check if the token is valid | ||
182 | 57 | if self._whoami is None: | ||
183 | 58 | return False | ||
184 | 59 | else: | ||
185 | 60 | return True | ||
186 | 61 | |||
187 | 62 | def clear_token(self): | ||
188 | 63 | clear_token_from_ubuntu_sso(SOFTWARE_CENTER_NAME_KEYRING) | ||
189 | 64 | |||
190 | 65 | def get_oauth_token_and_verify_sync(self): | ||
191 | 66 | token = self.get_oauth_token_sync() | ||
192 | 67 | # check if the token is valid and reset it if it is not | ||
193 | 68 | if token and not self.verify_token(token): | ||
194 | 69 | self.clear_token() | ||
195 | 70 | # re-trigger login | ||
196 | 71 | token = self.get_oauth_token_sync() | ||
197 | 72 | return token | ||
198 | 73 | |||
199 | 74 | def get_oauth_token_sync(self): | ||
200 | 75 | self.oauth = None | ||
201 | 76 | sso = get_sso_backend( | ||
202 | 77 | self.xid, | ||
203 | 78 | SOFTWARE_CENTER_NAME_KEYRING, | ||
204 | 79 | _(SOFTWARE_CENTER_SSO_DESCRIPTION)) | ||
205 | 80 | sso.connect("login-successful", self._login_successful) | ||
206 | 81 | sso.connect("login-failed", lambda s: self.loop.quit()) | ||
207 | 82 | sso.connect("login-canceled", lambda s: self.loop.quit()) | ||
208 | 83 | sso.login_or_register() | ||
209 | 84 | self.loop.run() | ||
210 | 85 | return self.oauth | ||
211 | 0 | 86 | ||
212 | === added file 'softwarecenter/backend/piston/ubuntusso_pristine.py' | |||
213 | --- softwarecenter/backend/piston/ubuntusso_pristine.py 1970-01-01 00:00:00 +0000 | |||
214 | +++ softwarecenter/backend/piston/ubuntusso_pristine.py 2012-01-05 15:35:32 +0000 | |||
215 | @@ -0,0 +1,22 @@ | |||
216 | 1 | from piston_mini_client import PistonAPI, returns_json | ||
217 | 2 | from piston_mini_client.validators import oauth_protected | ||
218 | 3 | |||
219 | 4 | # These are factored out as constants for if you need to work against a | ||
220 | 5 | # server that doesn't support both schemes (like http-only dev servers) | ||
221 | 6 | PUBLIC_API_SCHEME = 'http' | ||
222 | 7 | AUTHENTICATED_API_SCHEME = 'https' | ||
223 | 8 | |||
224 | 9 | |||
225 | 10 | # this is only here because: | ||
226 | 11 | # a) ubuntu-sso-client does not support verifying if the credentials | ||
227 | 12 | # are still valid | ||
228 | 13 | # b) the restful client interface is not really needed because we just | ||
229 | 14 | # need this one single call | ||
230 | 15 | class UbuntuSsoAPI(PistonAPI): | ||
231 | 16 | default_service_root = 'http://localhost:8000/api/2.0' | ||
232 | 17 | |||
233 | 18 | @oauth_protected | ||
234 | 19 | @returns_json | ||
235 | 20 | def whoami(self, id=None): | ||
236 | 21 | return self._get('accounts?ws.op=me', | ||
237 | 22 | scheme=AUTHENTICATED_API_SCHEME) | ||
238 | 0 | 23 | ||
239 | === added file 'softwarecenter/backend/recommends.py' | |||
240 | --- softwarecenter/backend/recommends.py 1970-01-01 00:00:00 +0000 | |||
241 | +++ softwarecenter/backend/recommends.py 2012-01-05 15:35:32 +0000 | |||
242 | @@ -0,0 +1,82 @@ | |||
243 | 1 | #!/usr/bin/python | ||
244 | 2 | # -*- coding: utf-8 -*- | ||
245 | 3 | |||
246 | 4 | # Copyright (C) 2012 Canonical | ||
247 | 5 | # | ||
248 | 6 | # Authors: | ||
249 | 7 | # Michael Vogt | ||
250 | 8 | # | ||
251 | 9 | # This program is free software; you can redistribute it and/or modify it under | ||
252 | 10 | # the terms of the GNU General Public License as published by the Free Software | ||
253 | 11 | # Foundation; version 3. | ||
254 | 12 | # | ||
255 | 13 | # This program is distributed in the hope that it will be useful, but WITHOUT | ||
256 | 14 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
257 | 15 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
258 | 16 | # details. | ||
259 | 17 | # | ||
260 | 18 | # You should have received a copy of the GNU General Public License along with | ||
261 | 19 | # this program; if not, write to the Free Software Foundation, Inc., | ||
262 | 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
263 | 21 | |||
264 | 22 | from gi.repository import GObject | ||
265 | 23 | import logging | ||
266 | 24 | import os | ||
267 | 25 | |||
268 | 26 | import softwarecenter.paths | ||
269 | 27 | from softwarecenter.paths import PistonHelpers | ||
270 | 28 | from spawn_helper import SpawnHelper | ||
271 | 29 | from softwarecenter.i18n import get_language | ||
272 | 30 | from softwarecenter.distro import get_distro, get_current_arch | ||
273 | 31 | |||
274 | 32 | LOG = logging.getLogger(__name__) | ||
275 | 33 | |||
276 | 34 | class RecommenderAgent(GObject.GObject): | ||
277 | 35 | |||
278 | 36 | __gsignals__ = { | ||
279 | 37 | "recommend-top" : (GObject.SIGNAL_RUN_LAST, | ||
280 | 38 | GObject.TYPE_NONE, | ||
281 | 39 | (GObject.TYPE_PYOBJECT,), | ||
282 | 40 | ), | ||
283 | 41 | "error" : (GObject.SIGNAL_RUN_LAST, | ||
284 | 42 | GObject.TYPE_NONE, | ||
285 | 43 | (str,), | ||
286 | 44 | ), | ||
287 | 45 | } | ||
288 | 46 | |||
289 | 47 | def __init__(self, xid=None): | ||
290 | 48 | GObject.GObject.__init__(self) | ||
291 | 49 | |||
292 | 50 | def query_recommend_top(self): | ||
293 | 51 | # build the command | ||
294 | 52 | spawner = SpawnHelper() | ||
295 | 53 | spawner.parent_xid = self.xid | ||
296 | 54 | spawner.connect("data-available", self._on_recommend_top_data) | ||
297 | 55 | spawner.connect("error", lambda spawner, err: self.emit("error", err)) | ||
298 | 56 | spawner.run_generic_piston_helper( | ||
299 | 57 | "SoftwareCenterRecommenderAPI", "recommend_top") | ||
300 | 58 | |||
301 | 59 | def _on_recommend_top_data(self, spawner, piston_top_apps): | ||
302 | 60 | self.emit("recommend-top", piston_top_apps) | ||
303 | 61 | |||
304 | 62 | |||
305 | 63 | if __name__ == "__main__": | ||
306 | 64 | from gi.repository import Gtk | ||
307 | 65 | |||
308 | 66 | def _recommend_top(agent, top_apps): | ||
309 | 67 | print ("_recommend_top: %s" % top_apps) | ||
310 | 68 | def _error(agent, msg): | ||
311 | 69 | print ("got a error: %s" % msg) | ||
312 | 70 | Gtk.main_quit() | ||
313 | 71 | |||
314 | 72 | # test specific stuff | ||
315 | 73 | logging.basicConfig() | ||
316 | 74 | softwarecenter.paths.datadir = "./data" | ||
317 | 75 | |||
318 | 76 | agent = RecommenderAgent() | ||
319 | 77 | agent.connect("recommend-top", _recommend_top) | ||
320 | 78 | agent.connect("error", _error) | ||
321 | 79 | agent.query_recommend_top() | ||
322 | 80 | |||
323 | 81 | |||
324 | 82 | Gtk.main() | ||
325 | 0 | 83 | ||
326 | === modified file 'softwarecenter/backend/reviews/__init__.py' | |||
327 | --- softwarecenter/backend/reviews/__init__.py 2011-11-07 10:04:33 +0000 | |||
328 | +++ softwarecenter/backend/reviews/__init__.py 2012-01-05 15:35:32 +0000 | |||
329 | @@ -19,6 +19,7 @@ | |||
330 | 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
331 | 20 | 20 | ||
332 | 21 | import datetime | 21 | import datetime |
333 | 22 | import json | ||
334 | 22 | import logging | 23 | import logging |
335 | 23 | import operator | 24 | import operator |
336 | 24 | import os | 25 | import os |
337 | @@ -107,13 +108,10 @@ | |||
338 | 107 | return False | 108 | return False |
339 | 108 | 109 | ||
340 | 109 | # run the command and add watcher | 110 | # run the command and add watcher |
341 | 110 | cmd = [os.path.join( | ||
342 | 111 | softwarecenter.paths.datadir, PistonHelpers.GET_USEFUL_VOTES), | ||
343 | 112 | "--username", user, | ||
344 | 113 | ] | ||
345 | 114 | spawn_helper = SpawnHelper() | 111 | spawn_helper = SpawnHelper() |
346 | 115 | spawn_helper.connect("data-available", self._on_usefulness_data) | 112 | spawn_helper.connect("data-available", self._on_usefulness_data) |
348 | 116 | spawn_helper.run(cmd) | 113 | spawn_helper.run_generic_piston_helper( |
349 | 114 | "RatingsAndReviewsAPI", "get_usefulness", username=user) | ||
350 | 117 | 115 | ||
351 | 118 | def _on_usefulness_data(self, spawn_helper, results): | 116 | def _on_usefulness_data(self, spawn_helper, results): |
352 | 119 | '''called if usefulness retrieved from server''' | 117 | '''called if usefulness retrieved from server''' |
353 | 120 | 118 | ||
354 | === modified file 'softwarecenter/backend/reviews/rnr.py' | |||
355 | --- softwarecenter/backend/reviews/rnr.py 2011-11-07 08:26:13 +0000 | |||
356 | +++ softwarecenter/backend/reviews/rnr.py 2012-01-05 15:35:32 +0000 | |||
357 | @@ -128,22 +128,20 @@ | |||
358 | 128 | except OSError: | 128 | except OSError: |
359 | 129 | days_delta = 0 | 129 | days_delta = 0 |
360 | 130 | LOG.debug("refresh with days_delta: %s" % days_delta) | 130 | LOG.debug("refresh with days_delta: %s" % days_delta) |
361 | 131 | # FIXME: the server currently has bug (#757695) so we | ||
362 | 132 | # can not turn this on just yet and need to use | ||
363 | 133 | # the old "catch-all" review-stats for now | ||
364 | 131 | #origin = "any" | 134 | #origin = "any" |
365 | 132 | #distroseries = self.distro.get_codename() | 135 | #distroseries = self.distro.get_codename() |
374 | 133 | cmd = [os.path.join( | 136 | spawn_helper = SpawnHelper() |
375 | 134 | softwarecenter.paths.datadir, PistonHelpers.GET_REVIEW_STATS), | 137 | spawn_helper.connect("data-available", self._on_review_stats_data, callback) |
368 | 135 | # FIXME: the server currently has bug (#757695) so we | ||
369 | 136 | # can not turn this on just yet and need to use | ||
370 | 137 | # the old "catch-all" review-stats for now | ||
371 | 138 | #"--origin", origin, | ||
372 | 139 | #"--distroseries", distroseries, | ||
373 | 140 | ] | ||
376 | 141 | if days_delta: | 138 | if days_delta: |
382 | 142 | cmd += ["--days-delta", str(days_delta)] | 139 | spawn_helper.run_generic_piston_helper( |
383 | 143 | spawn_helper = SpawnHelper() | 140 | "RatingsAndReviewsAPI", "review_stats", days=days_delta) |
384 | 144 | spawn_helper.connect("data-available", self._on_review_stats_data, callback) | 141 | else: |
385 | 145 | spawn_helper.run(cmd) | 142 | spawn_helper.run_generic_piston_helper( |
386 | 146 | 143 | "RatingsAndReviewsAPI", "review_stats") | |
387 | 144 | |||
388 | 147 | def _on_review_stats_data(self, spawn_helper, piston_review_stats, callback): | 145 | def _on_review_stats_data(self, spawn_helper, piston_review_stats, callback): |
389 | 148 | """ process stdout from the helper """ | 146 | """ process stdout from the helper """ |
390 | 149 | review_stats = self.REVIEW_STATS_CACHE | 147 | review_stats = self.REVIEW_STATS_CACHE |
391 | 150 | 148 | ||
392 | === modified file 'softwarecenter/backend/scagent.py' | |||
393 | --- softwarecenter/backend/scagent.py 2012-01-03 15:20:56 +0000 | |||
394 | +++ softwarecenter/backend/scagent.py 2012-01-05 15:35:32 +0000 | |||
395 | @@ -20,6 +20,7 @@ | |||
396 | 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
397 | 21 | 21 | ||
398 | 22 | from gi.repository import GObject | 22 | from gi.repository import GObject |
399 | 23 | import json | ||
400 | 23 | import logging | 24 | import logging |
401 | 24 | import os | 25 | import os |
402 | 25 | 26 | ||
403 | @@ -56,14 +57,7 @@ | |||
404 | 56 | GObject.GObject.__init__(self) | 57 | GObject.GObject.__init__(self) |
405 | 57 | self.distro = get_distro() | 58 | self.distro = get_distro() |
406 | 58 | self.ignore_cache = ignore_cache | 59 | self.ignore_cache = ignore_cache |
415 | 59 | binary = os.path.join( | 60 | self.xid = xid |
408 | 60 | softwarecenter.paths.datadir, PistonHelpers.SOFTWARE_CENTER_AGENT) | ||
409 | 61 | self.HELPER_CMD = [binary] | ||
410 | 62 | if self.ignore_cache: | ||
411 | 63 | self.HELPER_CMD.append("--ignore-cache") | ||
412 | 64 | if xid: | ||
413 | 65 | self.HELPER_CMD.append("--parent-xid") | ||
414 | 66 | self.HELPER_CMD.append(str(xid)) | ||
416 | 67 | 61 | ||
417 | 68 | def query_available(self, series_name=None, arch_tag=None): | 62 | def query_available(self, series_name=None, arch_tag=None): |
418 | 69 | self._query_available(series_name, arch_tag, for_qa=False) | 63 | self._query_available(series_name, arch_tag, for_qa=False) |
419 | @@ -78,41 +72,52 @@ | |||
420 | 78 | if not arch_tag: | 72 | if not arch_tag: |
421 | 79 | arch_tag = get_current_arch() | 73 | arch_tag = get_current_arch() |
422 | 80 | # build the command | 74 | # build the command |
424 | 81 | cmd = self.HELPER_CMD[:] | 75 | spawner = SpawnHelper() |
425 | 76 | spawner.parent_xid = self.xid | ||
426 | 77 | spawner.ignore_cache = self.ignore_cache | ||
427 | 78 | spawner.connect("data-available", self._on_query_available_data) | ||
428 | 79 | spawner.connect("error", lambda spawner, err: self.emit("error", err)) | ||
429 | 82 | if for_qa: | 80 | if for_qa: |
431 | 83 | cmd.append("available_apps_qa") | 81 | spawner.needs_auth = True |
432 | 82 | spawner.run_generic_piston_helper( | ||
433 | 83 | "SoftwareCenterAgentAPI", | ||
434 | 84 | "available_apps_qa", | ||
435 | 85 | lang=get_langugage(), | ||
436 | 86 | series=series_name, | ||
437 | 87 | arch=arch_tag) | ||
438 | 84 | else: | 88 | else: |
448 | 85 | cmd.append("available_apps") | 89 | spawner.run_generic_piston_helper( |
449 | 86 | cmd += [language, | 90 | "SoftwareCenterAgentAPI", |
450 | 87 | series_name, | 91 | "available_apps", |
451 | 88 | arch_tag, | 92 | lang=get_language(), |
452 | 89 | ] | 93 | series=series_name, |
453 | 90 | spawner = SpawnHelper() | 94 | arch=arch_tag) |
454 | 91 | spawner.connect("data-available", self._on_query_available_data) | 95 | |
446 | 92 | spawner.connect("error", lambda spawner, err: self.emit("error", err)) | ||
447 | 93 | spawner.run(cmd) | ||
455 | 94 | def _on_query_available_data(self, spawner, piston_available): | 96 | def _on_query_available_data(self, spawner, piston_available): |
456 | 95 | self.emit("available", piston_available) | 97 | self.emit("available", piston_available) |
457 | 96 | 98 | ||
458 | 97 | def query_available_for_me(self, oauth_token, openid_identifier): | 99 | def query_available_for_me(self, oauth_token, openid_identifier): |
459 | 98 | cmd = self.HELPER_CMD[:] | ||
460 | 99 | cmd.append("subscriptions_for_me") | ||
461 | 100 | spawner = SpawnHelper() | 100 | spawner = SpawnHelper() |
462 | 101 | spawner.parent_xid = self.xid | ||
463 | 102 | spawner.ignore_cache = self.ignore_cache | ||
464 | 101 | spawner.connect("data-available", self._on_query_available_for_me_data) | 103 | spawner.connect("data-available", self._on_query_available_for_me_data) |
465 | 102 | spawner.connect("error", lambda spawner, err: self.emit("error", err)) | 104 | spawner.connect("error", lambda spawner, err: self.emit("error", err)) |
467 | 103 | spawner.run(cmd) | 105 | spawner.needs_auth = True |
468 | 106 | spawner.run_generic_piston_helper( | ||
469 | 107 | "SoftwareCenterAgentAPI", "subscriptions_for_me") | ||
470 | 104 | def _on_query_available_for_me_data(self, spawner, piston_available_for_me): | 108 | def _on_query_available_for_me_data(self, spawner, piston_available_for_me): |
471 | 105 | self.emit("available-for-me", piston_available_for_me) | 109 | self.emit("available-for-me", piston_available_for_me) |
472 | 106 | 110 | ||
473 | 107 | def query_exhibits(self): | 111 | def query_exhibits(self): |
474 | 108 | cmd = self.HELPER_CMD[:] | ||
475 | 109 | cmd.append("exhibits") | ||
476 | 110 | cmd.append(get_language()) | ||
477 | 111 | cmd.append(self.distro.get_codename()) | ||
478 | 112 | spawner = SpawnHelper() | 112 | spawner = SpawnHelper() |
479 | 113 | spawner.parent_xid = self.xid | ||
480 | 114 | spawner.ignore_cache = self.ignore_cache | ||
481 | 113 | spawner.connect("data-available", self._on_exhibits_data_available) | 115 | spawner.connect("data-available", self._on_exhibits_data_available) |
482 | 114 | spawner.connect("error", lambda spawner, err: self.emit("error", err)) | 116 | spawner.connect("error", lambda spawner, err: self.emit("error", err)) |
484 | 115 | spawner.run(cmd) | 117 | spawner.run_generic_piston_helper( |
485 | 118 | "SoftwareCenterAgentAPI", "exhiits", | ||
486 | 119 | lang=get_language(), series=self.distro.get_codename()) | ||
487 | 120 | |||
488 | 116 | def _on_exhibits_data_available(self, spawner, exhibits): | 121 | def _on_exhibits_data_available(self, spawner, exhibits): |
489 | 117 | for exhibit in exhibits: | 122 | for exhibit in exhibits: |
490 | 118 | # special case, if there is no title provided by the server | 123 | # special case, if there is no title provided by the server |
491 | 119 | 124 | ||
492 | === modified file 'softwarecenter/backend/spawn_helper.py' | |||
493 | --- softwarecenter/backend/spawn_helper.py 2011-09-15 14:24:14 +0000 | |||
494 | +++ softwarecenter/backend/spawn_helper.py 2012-01-05 15:35:32 +0000 | |||
495 | @@ -30,6 +30,9 @@ | |||
496 | 30 | import os | 30 | import os |
497 | 31 | import json | 31 | import json |
498 | 32 | 32 | ||
499 | 33 | import softwarecenter.paths | ||
500 | 34 | from softwarecenter.paths import PistonHelpers | ||
501 | 35 | |||
502 | 33 | from gi.repository import GObject | 36 | from gi.repository import GObject |
503 | 34 | 37 | ||
504 | 35 | LOG = logging.getLogger(__name__) | 38 | LOG = logging.getLogger(__name__) |
505 | @@ -59,6 +62,27 @@ | |||
506 | 59 | self._io_watch = None | 62 | self._io_watch = None |
507 | 60 | self._child_watch = None | 63 | self._child_watch = None |
508 | 61 | self._cmd = None | 64 | self._cmd = None |
509 | 65 | self.needs_auth = False | ||
510 | 66 | self.ignore_cache = False | ||
511 | 67 | self.parent_xid = None | ||
512 | 68 | |||
513 | 69 | def run_generic_piston_helper(self, klass, func, **kwargs): | ||
514 | 70 | binary = os.path.join( | ||
515 | 71 | softwarecenter.paths.datadir, PistonHelpers.GENERIC_HELPER) | ||
516 | 72 | cmd = [binary] | ||
517 | 73 | cmd += ["--datadir", softwarecenter.paths.datadir] | ||
518 | 74 | if self.needs_auth: | ||
519 | 75 | cmd.append("--needs-auth") | ||
520 | 76 | if self.ignore_cache: | ||
521 | 77 | cmd.append("--ignore-cache") | ||
522 | 78 | if self.parent_xid: | ||
523 | 79 | cmd.append("--parent-xid") | ||
524 | 80 | cmd.append(str(xid)) | ||
525 | 81 | cmd += [klass, func] | ||
526 | 82 | if kwargs: | ||
527 | 83 | cmd.append(json.dumps(kwargs)) | ||
528 | 84 | LOG.debug("run_generic_piston_helper()") | ||
529 | 85 | self.run(cmd) | ||
530 | 62 | 86 | ||
531 | 63 | def run(self, cmd): | 87 | def run(self, cmd): |
532 | 64 | self._cmd = cmd | 88 | self._cmd = cmd |
533 | 65 | 89 | ||
534 | === renamed file 'softwarecenter/backend/restfulclient.py' => 'softwarecenter/backend/ubuntusso.py' | |||
535 | --- softwarecenter/backend/restfulclient.py 2012-01-05 09:53:52 +0000 | |||
536 | +++ softwarecenter/backend/ubuntusso.py 2012-01-05 15:35:32 +0000 | |||
537 | @@ -19,133 +19,27 @@ | |||
538 | 19 | # this program; if not, write to the Free Software Foundation, Inc., | 19 | # this program; if not, write to the Free Software Foundation, Inc., |
539 | 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 20 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
540 | 21 | 21 | ||
541 | 22 | import os | ||
542 | 23 | 22 | ||
543 | 24 | from gi.repository import GObject | 23 | from gi.repository import GObject |
544 | 25 | 24 | ||
545 | 26 | import logging | 25 | import logging |
561 | 27 | import threading | 26 | import os |
562 | 28 | 27 | ||
563 | 29 | from softwarecenter.enums import BUY_SOMETHING_HOST | 28 | import softwarecenter.paths |
564 | 30 | 29 | from softwarecenter.paths import PistonHelpers | |
565 | 31 | # possible workaround for bug #599332 is to try to import lazr.restful | 30 | |
566 | 32 | # import lazr.restful | 31 | import piston_mini_client.auth |
552 | 33 | # import lazr.restfulclient | ||
553 | 34 | |||
554 | 35 | from lazr.restfulclient.resource import ServiceRoot | ||
555 | 36 | from lazr.restfulclient.authorize import BasicHttpAuthorizer | ||
556 | 37 | from lazr.restfulclient.authorize.oauth import OAuthAuthorizer | ||
557 | 38 | from oauth.oauth import OAuthToken | ||
558 | 39 | |||
559 | 40 | from softwarecenter.paths import SOFTWARE_CENTER_CACHE_DIR | ||
560 | 41 | from Queue import Queue | ||
567 | 42 | 32 | ||
568 | 43 | # mostly for testing | 33 | # mostly for testing |
569 | 44 | from fake_review_settings import FakeReviewSettings, network_delay | 34 | from fake_review_settings import FakeReviewSettings, network_delay |
572 | 45 | 35 | from spawn_helper import SpawnHelper | |
573 | 46 | from login import LoginBackend | 36 | |
574 | 37 | from softwarecenter.utils import clear_token_from_ubuntu_sso | ||
575 | 38 | |||
576 | 39 | from gettext import gettext as _ | ||
577 | 47 | 40 | ||
578 | 48 | LOG = logging.getLogger(__name__) | 41 | LOG = logging.getLogger(__name__) |
579 | 49 | 42 | ||
580 | 50 | UBUNTU_SSO_SERVICE = os.environ.get( | ||
581 | 51 | "USSOC_SERVICE_URL", "https://login.ubuntu.com/api/1.0") | ||
582 | 52 | UBUNTU_SOFTWARE_CENTER_AGENT_SERVICE = BUY_SOMETHING_HOST+"/api/1.0" | ||
583 | 53 | |||
584 | 54 | class AttributesObject(object): | ||
585 | 55 | """ convinient object to hold attributes """ | ||
586 | 56 | MAX_REPR_STRING_SIZE = 30 | ||
587 | 57 | |||
588 | 58 | def __repr__(self): | ||
589 | 59 | s = "<'%s': " % self.__class__.__name__ | ||
590 | 60 | for key in vars(self): | ||
591 | 61 | value = str(getattr(self, key)) | ||
592 | 62 | if len(value) > self.MAX_REPR_STRING_SIZE: | ||
593 | 63 | value = "%s..." % value[:self.MAX_REPR_STRING_SIZE] | ||
594 | 64 | s += "%s='%s';" % (key, value) | ||
595 | 65 | s += ">" | ||
596 | 66 | return s | ||
597 | 67 | |||
598 | 68 | |||
599 | 69 | def restful_collection_to_real_python(restful_list): | ||
600 | 70 | """ take a restful and convert it to a python list with real python | ||
601 | 71 | objects | ||
602 | 72 | """ | ||
603 | 73 | l = [] | ||
604 | 74 | for entry in restful_list: | ||
605 | 75 | o = AttributesObject() | ||
606 | 76 | for attr in entry.lp_attributes: | ||
607 | 77 | setattr(o, attr, getattr(entry, attr)) | ||
608 | 78 | l.append(o) | ||
609 | 79 | return l | ||
610 | 80 | |||
611 | 81 | class RestfulClientWorker(threading.Thread): | ||
612 | 82 | """ a generic worker thread for a lazr.restfulclient """ | ||
613 | 83 | |||
614 | 84 | def __init__(self, authorizer, service_root): | ||
615 | 85 | """ init the thread """ | ||
616 | 86 | threading.Thread.__init__(self) | ||
617 | 87 | self._service_root_url = service_root | ||
618 | 88 | self._authorizer = authorizer | ||
619 | 89 | self._pending_requests = Queue() | ||
620 | 90 | self._shutdown = False | ||
621 | 91 | self.daemon = True | ||
622 | 92 | self.error = None | ||
623 | 93 | self._cachedir = os.path.join(SOFTWARE_CENTER_CACHE_DIR, | ||
624 | 94 | "restfulclient") | ||
625 | 95 | |||
626 | 96 | def run(self): | ||
627 | 97 | """ | ||
628 | 98 | Main thread run interface, logs into launchpad | ||
629 | 99 | """ | ||
630 | 100 | LOG.debug("lp worker thread run") | ||
631 | 101 | try: | ||
632 | 102 | self.service = ServiceRoot(self._authorizer, | ||
633 | 103 | self._service_root_url, | ||
634 | 104 | self._cachedir) | ||
635 | 105 | except: | ||
636 | 106 | logging.exception("worker thread can not connect to service root") | ||
637 | 107 | self.error = "ERROR_SERVICE_ROOT" | ||
638 | 108 | self._shutdown = True | ||
639 | 109 | return | ||
640 | 110 | # loop | ||
641 | 111 | self._wait_for_commands() | ||
642 | 112 | |||
643 | 113 | def shutdown(self): | ||
644 | 114 | """Request shutdown""" | ||
645 | 115 | self._shutdown = True | ||
646 | 116 | |||
647 | 117 | def queue_request(self, func, args, kwargs, result_callback, error_callback): | ||
648 | 118 | """ | ||
649 | 119 | queue a (remote) command for execution, the result_callback will | ||
650 | 120 | call with the result_list when done (that function will be | ||
651 | 121 | called async) | ||
652 | 122 | """ | ||
653 | 123 | self._pending_requests.put((func, args, kwargs, result_callback, error_callback)) | ||
654 | 124 | |||
655 | 125 | def _wait_for_commands(self): | ||
656 | 126 | """internal helper that waits for commands""" | ||
657 | 127 | while True: | ||
658 | 128 | while not self._pending_requests.empty(): | ||
659 | 129 | LOG.debug("found pending request") | ||
660 | 130 | (func_str, args, kwargs, result_callback, error_callback) = self._pending_requests.get() | ||
661 | 131 | # run func async | ||
662 | 132 | try: | ||
663 | 133 | func = self.service | ||
664 | 134 | for part in func_str.split("."): | ||
665 | 135 | func = getattr(func, part) | ||
666 | 136 | res = func(*args, **kwargs) | ||
667 | 137 | except Exception ,e: | ||
668 | 138 | error_callback(e) | ||
669 | 139 | else: | ||
670 | 140 | result_callback(res) | ||
671 | 141 | self._pending_requests.task_done() | ||
672 | 142 | # wait a bit | ||
673 | 143 | import time | ||
674 | 144 | time.sleep(0.1) | ||
675 | 145 | if (self._shutdown and | ||
676 | 146 | self._pending_requests.empty()): | ||
677 | 147 | return | ||
678 | 148 | |||
679 | 149 | class UbuntuSSOAPI(GObject.GObject): | 43 | class UbuntuSSOAPI(GObject.GObject): |
680 | 150 | 44 | ||
681 | 151 | __gsignals__ = { | 45 | __gsignals__ = { |
682 | @@ -162,46 +56,24 @@ | |||
683 | 162 | 56 | ||
684 | 163 | def __init__(self, token): | 57 | def __init__(self, token): |
685 | 164 | GObject.GObject.__init__(self) | 58 | GObject.GObject.__init__(self) |
689 | 165 | self._whoami = None | 59 | # FIXME: the token is not currently used as the helper will |
690 | 166 | self._error = None | 60 | # query the keyring again |
688 | 167 | self.service = UBUNTU_SSO_SERVICE | ||
691 | 168 | self.token = token | 61 | self.token = token |
721 | 169 | token = OAuthToken(self.token["token"], self.token["token_secret"]) | 62 | |
722 | 170 | authorizer = OAuthAuthorizer(self.token["consumer_key"], | 63 | def _on_whoami_data(self, spawner, piston_whoami): |
723 | 171 | self.token["consumer_secret"], | 64 | self.emit("whoami", piston_whoami) |
695 | 172 | access_token=token) | ||
696 | 173 | # we need to init the GObject.init_threads() | ||
697 | 174 | # - if we do it globally s-c will crash on exit (LP: #907568) | ||
698 | 175 | # - if we don't do it, s-c will hang in worker_thread.start() | ||
699 | 176 | # (even though threads_init is deprecated and according to the | ||
700 | 177 | # docs is run automatically nowdays) | ||
701 | 178 | # - if we do it here some apps will still crash | ||
702 | 179 | self.worker_thread = RestfulClientWorker(authorizer, self.service) | ||
703 | 180 | self.worker_thread.start() | ||
704 | 181 | GObject.timeout_add(200, self._monitor_thread) | ||
705 | 182 | |||
706 | 183 | def _monitor_thread(self): | ||
707 | 184 | # glib bit of the threading, runs in the main thread | ||
708 | 185 | if self._whoami is not None: | ||
709 | 186 | self.emit("whoami", self._whoami) | ||
710 | 187 | self._whoami = None | ||
711 | 188 | if self._error is not None: | ||
712 | 189 | self.emit("error", self._error) | ||
713 | 190 | self._error = None | ||
714 | 191 | return True | ||
715 | 192 | |||
716 | 193 | def _thread_whoami_done(self, result): | ||
717 | 194 | self._whoami = result | ||
718 | 195 | |||
719 | 196 | def _thread_whoami_error(self, e): | ||
720 | 197 | self._error = e | ||
724 | 198 | 65 | ||
725 | 199 | def whoami(self): | 66 | def whoami(self): |
726 | 67 | """ trigger request for the getting account information, this | ||
727 | 68 | will also verify if the current token is valid and if not | ||
728 | 69 | trigger a cleanup/re-authenticate | ||
729 | 70 | """ | ||
730 | 200 | LOG.debug("whoami called") | 71 | LOG.debug("whoami called") |
735 | 201 | self.worker_thread.queue_request("accounts.me", (), {}, | 72 | spawner = SpawnHelper() |
736 | 202 | self._thread_whoami_done, | 73 | spawner.connect("data-available", self._on_whoami_data) |
737 | 203 | self._thread_whoami_error) | 74 | spawner.connect("error", lambda spawner, err: self.emit("error", err)) |
738 | 204 | 75 | spawner.needs_auth = True | |
739 | 76 | spawner.run_generic_piston_helper("UbuntuSsoAPI", "whoami") | ||
740 | 205 | 77 | ||
741 | 206 | class UbuntuSSOAPIFake(UbuntuSSOAPI): | 78 | class UbuntuSSOAPIFake(UbuntuSSOAPI): |
742 | 207 | 79 | ||
743 | @@ -243,71 +115,6 @@ | |||
744 | 243 | return ubuntu_sso_class | 115 | return ubuntu_sso_class |
745 | 244 | 116 | ||
746 | 245 | 117 | ||
747 | 246 | class UbuntuSSOlogin(LoginBackend): | ||
748 | 247 | |||
749 | 248 | NEW_ACCOUNT_URL = "https://login.launchpad.net/+standalone-login" | ||
750 | 249 | FORGOT_PASSWORD_URL = "https://login.ubuntu.com/+forgot_password" | ||
751 | 250 | |||
752 | 251 | SSO_AUTHENTICATE_FUNC = "authentications.authenticate" | ||
753 | 252 | |||
754 | 253 | def __init__(self): | ||
755 | 254 | LoginBackend.__init__(self) | ||
756 | 255 | self.service = UBUNTU_SSO_SERVICE | ||
757 | 256 | # we get a dict here with the following keys: | ||
758 | 257 | # token | ||
759 | 258 | # consumer_key (also the openid identifier) | ||
760 | 259 | # consumer_secret | ||
761 | 260 | # token_secret | ||
762 | 261 | # name (that is just 'software-center') | ||
763 | 262 | self.oauth_credentials = None | ||
764 | 263 | self._oauth_credentials = None | ||
765 | 264 | self._login_failure = None | ||
766 | 265 | self.worker_thread = None | ||
767 | 266 | |||
768 | 267 | def shutdown(self): | ||
769 | 268 | self.worker_thread.shutdown() | ||
770 | 269 | |||
771 | 270 | def login(self, username=None, password=None): | ||
772 | 271 | if not username or not password: | ||
773 | 272 | self.emit("need-username-password") | ||
774 | 273 | return | ||
775 | 274 | authorizer = BasicHttpAuthorizer(username, password) | ||
776 | 275 | self.worker_thread = RestfulClientWorker(authorizer, self.service) | ||
777 | 276 | self.worker_thread.start() | ||
778 | 277 | kwargs = { "token_name" : "software-center", | ||
779 | 278 | } | ||
780 | 279 | self.worker_thread.queue_request(self.SSO_AUTHENTICATE_FUNC, (), kwargs, | ||
781 | 280 | self._thread_authentication_done, | ||
782 | 281 | self._thread_authentication_error) | ||
783 | 282 | GObject.timeout_add(200, self._monitor_thread) | ||
784 | 283 | |||
785 | 284 | def _monitor_thread(self): | ||
786 | 285 | # glib bit of the threading, runs in the main thread | ||
787 | 286 | if self._oauth_credentials: | ||
788 | 287 | self.emit("login-successful", self._oauth_credentials) | ||
789 | 288 | self.oauth_credentials = self._oauth_credentials | ||
790 | 289 | self._oauth_credentials = None | ||
791 | 290 | if self._login_failure: | ||
792 | 291 | self.emit("login-failed") | ||
793 | 292 | self._login_failure = None | ||
794 | 293 | return True | ||
795 | 294 | |||
796 | 295 | def _thread_authentication_done(self, result): | ||
797 | 296 | # runs in the thread context, can not touch gui or glib | ||
798 | 297 | #print "_authentication_done", result | ||
799 | 298 | self._oauth_credentials = result | ||
800 | 299 | |||
801 | 300 | def _thread_authentication_error(self, e): | ||
802 | 301 | # runs in the thread context, can not touch gui or glib | ||
803 | 302 | #print "_authentication_error", type(e) | ||
804 | 303 | self._login_failure = e | ||
805 | 304 | |||
806 | 305 | def __del__(self): | ||
807 | 306 | #print "del" | ||
808 | 307 | if self.worker_thread: | ||
809 | 308 | self.worker_thread.shutdown() | ||
810 | 309 | |||
811 | 310 | |||
812 | 311 | # test code | 118 | # test code |
813 | 312 | def _login_success(lp, token): | 119 | def _login_success(lp, token): |
814 | 313 | print "success", lp, token | 120 | print "success", lp, token |
815 | @@ -323,40 +130,30 @@ | |||
816 | 323 | password = sys.stdin.readline().strip() | 130 | password = sys.stdin.readline().strip() |
817 | 324 | sso.login(user, password) | 131 | sso.login(user, password) |
818 | 325 | 132 | ||
819 | 326 | def _error(scaagent, errormsg): | ||
820 | 327 | print "_error:", errormsg | ||
821 | 328 | def _whoami(sso, whoami): | ||
822 | 329 | print "whoami: ", whoami | ||
823 | 330 | |||
824 | 331 | # interactive test code | 133 | # interactive test code |
825 | 332 | if __name__ == "__main__": | 134 | if __name__ == "__main__": |
826 | 135 | def _whoami(sso, result): | ||
827 | 136 | print "res: ", result | ||
828 | 137 | Gtk.main_quit() | ||
829 | 138 | def _error(sso, result): | ||
830 | 139 | print "err: ", result | ||
831 | 140 | Gtk.main_quit() | ||
832 | 141 | def _dbus_maybe_login_successful(ssologin, oauth_result): | ||
833 | 142 | print "got token, verify it now" | ||
834 | 143 | sso = UbuntuSSOAPI(oauth_result) | ||
835 | 144 | sso.connect("whoami", _whoami) | ||
836 | 145 | sso.connect("error", _error) | ||
837 | 146 | sso.whoami() | ||
838 | 147 | |||
839 | 148 | from gi.repository import Gtk | ||
840 | 333 | import sys | 149 | import sys |
841 | 334 | logging.basicConfig(level=logging.DEBUG) | 150 | logging.basicConfig(level=logging.DEBUG) |
868 | 335 | 151 | softwarecenter.paths.datadir = "./data" | |
869 | 336 | if len(sys.argv) < 2: | 152 | |
870 | 337 | print "need an argument, one of: 'sso', 'ssologin'" | 153 | from login_sso import get_sso_backend |
871 | 338 | sys.exit(1) | 154 | backend = get_sso_backend("", "appname", "help_text") |
872 | 339 | 155 | backend.connect("login-successful", _dbus_maybe_login_successful) | |
873 | 340 | elif sys.argv[1] == "sso": | 156 | backend.login_or_register() |
874 | 341 | def _dbus_maybe_login_successful(ssologin, oauth_result): | 157 | Gtk.main() |
849 | 342 | sso = UbuntuSSOAPI(oauth_result) | ||
850 | 343 | sso.connect("whoami", _whoami) | ||
851 | 344 | sso.connect("error", _error) | ||
852 | 345 | sso.whoami() | ||
853 | 346 | from login_sso import get_sso_backend | ||
854 | 347 | backend = get_sso_backend("", "appname", "help_text") | ||
855 | 348 | backend.connect("login-successful", _dbus_maybe_login_successful) | ||
856 | 349 | backend.login_or_register() | ||
857 | 350 | |||
858 | 351 | elif sys.argv[1] == "ssologin": | ||
859 | 352 | ssologin = UbuntuSSOlogin() | ||
860 | 353 | ssologin.connect("login-successful", _login_success) | ||
861 | 354 | ssologin.connect("login-failed", _login_failed) | ||
862 | 355 | ssologin.connect("need-username-password", _login_need_user_and_password) | ||
863 | 356 | ssologin.login() | ||
864 | 357 | |||
865 | 358 | else: | ||
866 | 359 | print "unknown option" | ||
867 | 360 | sys.exit(1) | ||
875 | 361 | 158 | ||
876 | 362 | 159 | ||
877 | 363 | 160 | ||
878 | === modified file 'softwarecenter/enums.py' | |||
879 | --- softwarecenter/enums.py 2011-12-16 12:50:18 +0000 | |||
880 | +++ softwarecenter/enums.py 2012-01-05 15:35:32 +0000 | |||
881 | @@ -34,6 +34,15 @@ | |||
882 | 34 | BUY_SOMETHING_HOST = os.environ.get("SOFTWARE_CENTER_BUY_HOST") or "https://software-center.ubuntu.com" | 34 | BUY_SOMETHING_HOST = os.environ.get("SOFTWARE_CENTER_BUY_HOST") or "https://software-center.ubuntu.com" |
883 | 35 | BUY_SOMETHING_HOST_ANONYMOUS = os.environ.get("SOFTWARE_CENTER_BUY_HOST") or "http://software-center.ubuntu.com" | 35 | BUY_SOMETHING_HOST_ANONYMOUS = os.environ.get("SOFTWARE_CENTER_BUY_HOST") or "http://software-center.ubuntu.com" |
884 | 36 | 36 | ||
885 | 37 | # recommender | ||
886 | 38 | RECOMMENDER_HOST = os.environ.get("SOFTWARE_CENTER_RECOMMENDER_HOST") or "https://recommender.software-center.ubuntu.com" | ||
887 | 39 | |||
888 | 40 | # for the sso login | ||
889 | 41 | UBUNTU_SSO_SERVICE = os.environ.get( | ||
890 | 42 | "USSOC_SERVICE_URL", "https://login.ubuntu.com/") | ||
891 | 43 | SSO_LOGIN_HOST = UBUNTU_SSO_SERVICE | ||
892 | 44 | |||
893 | 45 | |||
894 | 37 | # version of the database, every time something gets added (like | 46 | # version of the database, every time something gets added (like |
895 | 38 | # terms for mime-type) increase this (but keep as a string!) | 47 | # terms for mime-type) increase this (but keep as a string!) |
896 | 39 | DB_SCHEMA_VERSION = "6" | 48 | DB_SCHEMA_VERSION = "6" |
897 | 40 | 49 | ||
898 | === modified file 'softwarecenter/paths.py' | |||
899 | --- softwarecenter/paths.py 2011-09-13 12:45:56 +0000 | |||
900 | +++ softwarecenter/paths.py 2012-01-05 15:35:32 +0000 | |||
901 | @@ -86,9 +86,7 @@ | |||
902 | 86 | # piston helpers | 86 | # piston helpers |
903 | 87 | class PistonHelpers: | 87 | class PistonHelpers: |
904 | 88 | GET_REVIEWS = "piston_get_reviews_helper.py" | 88 | GET_REVIEWS = "piston_get_reviews_helper.py" |
908 | 89 | GET_REVIEW_STATS = "piston_get_review_stats_helper.py" | 89 | GENERIC_HELPER = "piston_generic_helper.py" |
906 | 90 | GET_USEFUL_VOTES = "piston_get_useful_votes_helper.py" | ||
907 | 91 | SOFTWARE_CENTER_AGENT = "piston_get_scagent_available_apps.py" | ||
909 | 92 | 90 | ||
910 | 93 | X2GO_HELPER = "x2go_helper.py" | 91 | X2GO_HELPER = "x2go_helper.py" |
911 | 94 | 92 | ||
912 | 95 | 93 | ||
913 | === modified file 'test/gtk3/test_views.py' | |||
914 | --- test/gtk3/test_views.py 2011-11-15 10:54:54 +0000 | |||
915 | +++ test/gtk3/test_views.py 2012-01-05 15:35:32 +0000 | |||
916 | @@ -13,6 +13,8 @@ | |||
917 | 13 | 13 | ||
918 | 14 | import softwarecenter.paths | 14 | import softwarecenter.paths |
919 | 15 | softwarecenter.paths.datadir = "../data" | 15 | softwarecenter.paths.datadir = "../data" |
920 | 16 | import os | ||
921 | 17 | os.environ["PYTHONPATH"] = "../" | ||
922 | 16 | 18 | ||
923 | 17 | class TestViews(unittest.TestCase): | 19 | class TestViews(unittest.TestCase): |
924 | 18 | 20 | ||
925 | 19 | 21 | ||
926 | === modified file 'test/test_reviews.py' | |||
927 | --- test/test_reviews.py 2011-11-04 14:52:52 +0000 | |||
928 | +++ test/test_reviews.py 2012-01-05 15:35:32 +0000 | |||
929 | @@ -11,6 +11,7 @@ | |||
930 | 11 | 11 | ||
931 | 12 | import softwarecenter.paths | 12 | import softwarecenter.paths |
932 | 13 | softwarecenter.paths.SOFTWARE_CENTER_CACHE_DIR = tempfile.mkdtemp() | 13 | softwarecenter.paths.SOFTWARE_CENTER_CACHE_DIR = tempfile.mkdtemp() |
933 | 14 | softwarecenter.paths.datadir = "../data" | ||
934 | 14 | 15 | ||
935 | 15 | from softwarecenter.backend.reviews.rnr import ( | 16 | from softwarecenter.backend.reviews.rnr import ( |
936 | 16 | ReviewLoaderSpawningRNRClient as ReviewLoader) | 17 | ReviewLoaderSpawningRNRClient as ReviewLoader) |
937 | @@ -51,6 +52,6 @@ | |||
938 | 51 | main_loop.iteration() | 52 | main_loop.iteration() |
939 | 52 | 53 | ||
940 | 53 | if __name__ == "__main__": | 54 | if __name__ == "__main__": |
943 | 54 | #import logging | 55 | import logging |
944 | 55 | #logging.basicConfig(level=logging.DEBUG) | 56 | logging.basicConfig(level=logging.DEBUG) |
945 | 56 | unittest.main() | 57 | unittest.main() |
946 | 57 | 58 | ||
947 | === modified file 'test/test_ubuntu_sso_api.py' | |||
948 | --- test/test_ubuntu_sso_api.py 2011-06-11 07:59:59 +0000 | |||
949 | +++ test/test_ubuntu_sso_api.py 2012-01-05 15:35:32 +0000 | |||
950 | @@ -6,10 +6,10 @@ | |||
951 | 6 | 6 | ||
952 | 7 | import os | 7 | import os |
953 | 8 | import unittest | 8 | import unittest |
958 | 9 | from softwarecenter.backend.restfulclient import (UbuntuSSOAPIFake, | 9 | from softwarecenter.backend.ubuntusso import (UbuntuSSOAPIFake, |
959 | 10 | UbuntuSSOAPI, | 10 | UbuntuSSOAPI, |
960 | 11 | get_ubuntu_sso_backend, | 11 | get_ubuntu_sso_backend, |
961 | 12 | ) | 12 | ) |
962 | 13 | 13 | ||
963 | 14 | class TestSSOAPI(unittest.TestCase): | 14 | class TestSSOAPI(unittest.TestCase): |
964 | 15 | """ tests the ubuntu sso backend stuff """ | 15 | """ tests the ubuntu sso backend stuff """ |
965 | 16 | 16 | ||
966 | === added file 'utils/piston-helpers/piston_generic_helper.py' | |||
967 | --- utils/piston-helpers/piston_generic_helper.py 1970-01-01 00:00:00 +0000 | |||
968 | +++ utils/piston-helpers/piston_generic_helper.py 2012-01-05 15:35:32 +0000 | |||
969 | @@ -0,0 +1,215 @@ | |||
970 | 1 | #!/usr/bin/python | ||
971 | 2 | # Copyright (C) 2011 Canonical | ||
972 | 3 | # | ||
973 | 4 | # Authors: | ||
974 | 5 | # Michael Vogt | ||
975 | 6 | # | ||
976 | 7 | # This program is free software; you can redistribute it and/or modify it under | ||
977 | 8 | # the terms of the GNU General Public License as published by the Free Software | ||
978 | 9 | # Foundation; version 3. | ||
979 | 10 | # | ||
980 | 11 | # This program is distributed in the hope that it will be useful, but WITHOUT | ||
981 | 12 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
982 | 13 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | ||
983 | 14 | # details. | ||
984 | 15 | # | ||
985 | 16 | # You should have received a copy of the GNU General Public License along with | ||
986 | 17 | # this program; if not, write to the Free Software Foundation, Inc., | ||
987 | 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
988 | 19 | |||
989 | 20 | import argparse | ||
990 | 21 | import logging | ||
991 | 22 | import os | ||
992 | 23 | import json | ||
993 | 24 | import pickle | ||
994 | 25 | import sys | ||
995 | 26 | |||
996 | 27 | from gi.repository import GObject | ||
997 | 28 | |||
998 | 29 | # useful for debugging | ||
999 | 30 | if "SOFTWARE_CENTER_DEBUG_HTTP" in os.environ: | ||
1000 | 31 | import httplib2 | ||
1001 | 32 | httplib2.debuglevel = 1 | ||
1002 | 33 | |||
1003 | 34 | import piston_mini_client.auth | ||
1004 | 35 | |||
1005 | 36 | try: | ||
1006 | 37 | import softwarecenter | ||
1007 | 38 | except ImportError: | ||
1008 | 39 | if os.path.exists("../softwarecenter"): | ||
1009 | 40 | sys.path.insert(0, "../") | ||
1010 | 41 | else: | ||
1011 | 42 | sys.path.insert(0, "/usr/share/software-center") | ||
1012 | 43 | |||
1013 | 44 | import softwarecenter.paths | ||
1014 | 45 | from softwarecenter.paths import SOFTWARE_CENTER_CACHE_DIR | ||
1015 | 46 | from softwarecenter.backend.login_sso import get_sso_backend | ||
1016 | 47 | |||
1017 | 48 | from softwarecenter.enums import (SOFTWARE_CENTER_NAME_KEYRING, | ||
1018 | 49 | SOFTWARE_CENTER_SSO_DESCRIPTION, | ||
1019 | 50 | ) | ||
1020 | 51 | |||
1021 | 52 | # the piston import | ||
1022 | 53 | from softwarecenter.backend.piston.ubuntusso_pristine import UbuntuSsoAPI | ||
1023 | 54 | from softwarecenter.backend.piston.rnrclient import RatingsAndReviewsAPI | ||
1024 | 55 | from softwarecenter.backend.piston.scaclient import SoftwareCenterAgentAPI | ||
1025 | 56 | from softwarecenter.backend.piston.sreclient_pristine import SoftwareCenterRecommenderAPI | ||
1026 | 57 | |||
1027 | 58 | # patch default_service_root to the one we use | ||
1028 | 59 | from softwarecenter.enums import SSO_LOGIN_HOST | ||
1029 | 60 | UbuntuSsoAPI.default_service_root = SSO_LOGIN_HOST+"/api/1.0" | ||
1030 | 61 | |||
1031 | 62 | from softwarecenter.enums import RECOMMENDER_HOST | ||
1032 | 63 | SoftwareCenterRecommenderAPI.default_service_root = RECOMMENDER_HOST+"/api/1.0" | ||
1033 | 64 | |||
1034 | 65 | |||
1035 | 66 | RatingsAndReviewsAPI # pyflakes | ||
1036 | 67 | UbuntuSsoAPI # pyflakes | ||
1037 | 68 | SoftwareCenterAgentAPI # pyflakes | ||
1038 | 69 | SoftwareCenterRecommenderAPI | ||
1039 | 70 | |||
1040 | 71 | from gettext import gettext as _ | ||
1041 | 72 | |||
1042 | 73 | # helper that is only used to verify that the token is ok | ||
1043 | 74 | # and trigger cleanup if not | ||
1044 | 75 | class SSOLoginHelper(object): | ||
1045 | 76 | |||
1046 | 77 | def __init__(self, xid=0): | ||
1047 | 78 | self.oauth = None | ||
1048 | 79 | self.xid = xid | ||
1049 | 80 | self.loop = GObject.MainLoop(GObject.main_context_default()) | ||
1050 | 81 | |||
1051 | 82 | def _login_successful(self, sso_backend, oauth_result): | ||
1052 | 83 | LOG.debug("_login_successful") | ||
1053 | 84 | self.oauth = oauth_result | ||
1054 | 85 | # FIXME: actually verify the token against ubuntu SSO | ||
1055 | 86 | self.loop.quit() | ||
1056 | 87 | |||
1057 | 88 | def verify_token_sync(self, token): | ||
1058 | 89 | LOG.debug("verify_token") | ||
1059 | 90 | auth = piston_mini_client.auth.OAuthAuthorizer(token["token"], | ||
1060 | 91 | token["token_secret"], | ||
1061 | 92 | token["consumer_key"], | ||
1062 | 93 | token["consumer_secret"]) | ||
1063 | 94 | api = UbuntuSsoAPI(auth=auth) | ||
1064 | 95 | try: | ||
1065 | 96 | res = api.whoami() | ||
1066 | 97 | except: | ||
1067 | 98 | LOG.exception("api.whoami failed") | ||
1068 | 99 | return None | ||
1069 | 100 | return res | ||
1070 | 101 | |||
1071 | 102 | def clear_token(self): | ||
1072 | 103 | clear_token_from_ubuntu_sso(SOFTWARE_CENTER_NAME_KEYRING) | ||
1073 | 104 | |||
1074 | 105 | def get_oauth_token_sync(self): | ||
1075 | 106 | self.oauth = None | ||
1076 | 107 | sso = get_sso_backend( | ||
1077 | 108 | self.xid, | ||
1078 | 109 | SOFTWARE_CENTER_NAME_KEYRING, | ||
1079 | 110 | _(SOFTWARE_CENTER_SSO_DESCRIPTION)) | ||
1080 | 111 | sso.connect("login-successful", self._login_successful) | ||
1081 | 112 | sso.connect("login-failed", lambda s: self.loop.quit()) | ||
1082 | 113 | sso.connect("login-canceled", lambda s: self.loop.quit()) | ||
1083 | 114 | sso.login_or_register() | ||
1084 | 115 | self.loop.run() | ||
1085 | 116 | return self.oauth | ||
1086 | 117 | |||
1087 | 118 | def get_oauth_token_and_verify_sync(self): | ||
1088 | 119 | token = self.get_oauth_token_sync() | ||
1089 | 120 | # check if the token is valid and reset it if it is not | ||
1090 | 121 | if token and not self.verify_token_sync(token): | ||
1091 | 122 | self.clear_token() | ||
1092 | 123 | # re-trigger login | ||
1093 | 124 | token = self.get_oauth_token_sync() | ||
1094 | 125 | return token | ||
1095 | 126 | |||
1096 | 127 | |||
1097 | 128 | |||
1098 | 129 | LOG = logging.getLogger(__name__) | ||
1099 | 130 | |||
1100 | 131 | if __name__ == "__main__": | ||
1101 | 132 | logging.basicConfig() | ||
1102 | 133 | |||
1103 | 134 | # command line parser | ||
1104 | 135 | parser = argparse.ArgumentParser( | ||
1105 | 136 | description="Backend helper for piston-mini-client based APIs") | ||
1106 | 137 | parser.add_argument("--debug", action="store_true", default=False, | ||
1107 | 138 | help="enable debug output") | ||
1108 | 139 | parser.add_argument("--datadir", default="/usr/share/software-center", | ||
1109 | 140 | help="setup alternative datadir") | ||
1110 | 141 | parser.add_argument("--ignore-cache", action="store_true", default=False, | ||
1111 | 142 | help="force ignore cache") | ||
1112 | 143 | parser.add_argument("--needs-auth", default=False, action="store_true", | ||
1113 | 144 | help="need oauth credentials") | ||
1114 | 145 | parser.add_argument("--output", default="pickle", | ||
1115 | 146 | help="output result as [pickle|json|text]") | ||
1116 | 147 | parser.add_argument("--parent-xid", default=0, | ||
1117 | 148 | help="xid of the parent window") | ||
1118 | 149 | parser.add_argument('klass', help='class to use') | ||
1119 | 150 | parser.add_argument('function', help='function to call') | ||
1120 | 151 | parser.add_argument('kwargs', nargs="?", | ||
1121 | 152 | help='kwargs for the function call as json') | ||
1122 | 153 | args = parser.parse_args() | ||
1123 | 154 | |||
1124 | 155 | if args.debug: | ||
1125 | 156 | logging.basicConfig(level=logging.DEBUG) | ||
1126 | 157 | LOG.setLevel(logging.DEBUG) | ||
1127 | 158 | |||
1128 | 159 | if args.ignore_cache: | ||
1129 | 160 | cachedir = None | ||
1130 | 161 | else: | ||
1131 | 162 | cachedir = os.path.join(SOFTWARE_CENTER_CACHE_DIR, "piston-helper") | ||
1132 | 163 | |||
1133 | 164 | # check what we need to call | ||
1134 | 165 | klass = globals()[args.klass] | ||
1135 | 166 | func = args.function | ||
1136 | 167 | kwargs = json.loads(args.kwargs or '{}') | ||
1137 | 168 | |||
1138 | 169 | softwarecenter.paths.datadir = args.datadir | ||
1139 | 170 | |||
1140 | 171 | if args.needs_auth: | ||
1141 | 172 | helper = SSOLoginHelper(args.parent_xid) | ||
1142 | 173 | token = helper.get_oauth_token_and_verify_sync() | ||
1143 | 174 | # if we don't have a token, error here | ||
1144 | 175 | if not token: | ||
1145 | 176 | sys.stderr.write("ERROR: can not obtain a oauth token\n") | ||
1146 | 177 | sys.exit(1) | ||
1147 | 178 | |||
1148 | 179 | auth = piston_mini_client.auth.OAuthAuthorizer(token["token"], | ||
1149 | 180 | token["token_secret"], | ||
1150 | 181 | token["consumer_key"], | ||
1151 | 182 | token["consumer_secret"]) | ||
1152 | 183 | api = klass(cachedir=cachedir, auth=auth) | ||
1153 | 184 | else: | ||
1154 | 185 | api = klass() | ||
1155 | 186 | |||
1156 | 187 | piston_reply = None | ||
1157 | 188 | # handle the args | ||
1158 | 189 | f = getattr(api, func) | ||
1159 | 190 | try: | ||
1160 | 191 | piston_reply = f(**kwargs) | ||
1161 | 192 | except: | ||
1162 | 193 | LOG.exception("urclient_apps") | ||
1163 | 194 | sys.exit(1) | ||
1164 | 195 | |||
1165 | 196 | # print to stdout where its consumed by the parent | ||
1166 | 197 | if piston_reply is None: | ||
1167 | 198 | LOG.warn("no data") | ||
1168 | 199 | sys.exit(0) | ||
1169 | 200 | |||
1170 | 201 | # check what format to use | ||
1171 | 202 | if args.output == "pickle": | ||
1172 | 203 | res = pickle.dumps(piston_reply) | ||
1173 | 204 | elif args.output == "json": | ||
1174 | 205 | res = json.dumps(piston_reply) | ||
1175 | 206 | elif args.output == "text": | ||
1176 | 207 | res = piston_reply | ||
1177 | 208 | |||
1178 | 209 | # and output it | ||
1179 | 210 | try: | ||
1180 | 211 | print res | ||
1181 | 212 | except IOError: | ||
1182 | 213 | # this can happen if the parent gets killed, no need to trigger | ||
1183 | 214 | # apport for this | ||
1184 | 215 | pass | ||
1185 | 0 | 216 | ||
1186 | === removed file 'utils/piston-helpers/piston_get_review_stats_helper.py' | |||
1187 | --- utils/piston-helpers/piston_get_review_stats_helper.py 2011-07-13 14:25:55 +0000 | |||
1188 | +++ utils/piston-helpers/piston_get_review_stats_helper.py 1970-01-01 00:00:00 +0000 | |||
1189 | @@ -1,65 +0,0 @@ | |||
1190 | 1 | #!/usr/bin/python | ||
1191 | 2 | |||
1192 | 3 | import os | ||
1193 | 4 | import pickle | ||
1194 | 5 | import logging | ||
1195 | 6 | import sys | ||
1196 | 7 | |||
1197 | 8 | from optparse import OptionParser | ||
1198 | 9 | |||
1199 | 10 | from softwarecenter.paths import SOFTWARE_CENTER_CACHE_DIR | ||
1200 | 11 | from softwarecenter.backend.piston.rnrclient import RatingsAndReviewsAPI | ||
1201 | 12 | |||
1202 | 13 | LOG = logging.getLogger(__name__) | ||
1203 | 14 | |||
1204 | 15 | if __name__ == "__main__": | ||
1205 | 16 | logging.basicConfig() | ||
1206 | 17 | |||
1207 | 18 | # common options for optparse go here | ||
1208 | 19 | parser = OptionParser() | ||
1209 | 20 | |||
1210 | 21 | # check options | ||
1211 | 22 | parser.add_option("--origin", default="any") | ||
1212 | 23 | parser.add_option("--distroseries", default="any") | ||
1213 | 24 | parser.add_option("--days-delta", default=None) | ||
1214 | 25 | parser.add_option("--debug", | ||
1215 | 26 | action="store_true", default=False) | ||
1216 | 27 | parser.add_option("--no-pickle", | ||
1217 | 28 | action="store_true", default=False) | ||
1218 | 29 | (options, args) = parser.parse_args() | ||
1219 | 30 | |||
1220 | 31 | if options.debug: | ||
1221 | 32 | LOG.setLevel(logging.DEBUG) | ||
1222 | 33 | |||
1223 | 34 | cachedir = os.path.join(SOFTWARE_CENTER_CACHE_DIR, "rnrclient") | ||
1224 | 35 | rnrclient = RatingsAndReviewsAPI(cachedir=cachedir) | ||
1225 | 36 | |||
1226 | 37 | kwargs = {"origin": options.origin, | ||
1227 | 38 | "distroseries": options.distroseries, | ||
1228 | 39 | } | ||
1229 | 40 | if options.days_delta: | ||
1230 | 41 | kwargs["days"] = int(options.days_delta) | ||
1231 | 42 | |||
1232 | 43 | # depending on the time delta, use a different call | ||
1233 | 44 | piston_review_stats = [] | ||
1234 | 45 | try: | ||
1235 | 46 | piston_review_stats = rnrclient.review_stats(**kwargs) | ||
1236 | 47 | except: | ||
1237 | 48 | LOG.exception("get_review_stats") | ||
1238 | 49 | sys.exit(1) | ||
1239 | 50 | |||
1240 | 51 | # useful for debugging | ||
1241 | 52 | if options.no_pickle: | ||
1242 | 53 | print "\n".join(["pkgname=%s total=%s avg=%s" % (s.package_name, | ||
1243 | 54 | s.ratings_total, | ||
1244 | 55 | s.ratings_average) | ||
1245 | 56 | for s in piston_review_stats]) | ||
1246 | 57 | else: | ||
1247 | 58 | # print to stdout where its consumed by the parent | ||
1248 | 59 | try: | ||
1249 | 60 | print pickle.dumps(piston_review_stats) | ||
1250 | 61 | except IOError: | ||
1251 | 62 | # this can happen if the parent gets killed, no need to trigger | ||
1252 | 63 | # apport for this | ||
1253 | 64 | sys.exit(1) | ||
1254 | 65 | |||
1255 | 66 | 0 | ||
1256 | === renamed file 'utils/piston-helpers/piston_get_scagent_available_apps.py' => 'utils/piston-helpers/piston_get_scagent_available_apps.py.THIS' | |||
1257 | --- utils/piston-helpers/piston_get_scagent_available_apps.py 2012-01-05 09:53:52 +0000 | |||
1258 | +++ utils/piston-helpers/piston_get_scagent_available_apps.py.THIS 2012-01-05 15:35:32 +0000 | |||
1259 | @@ -10,64 +10,14 @@ | |||
1260 | 10 | 10 | ||
1261 | 11 | import piston_mini_client.auth | 11 | import piston_mini_client.auth |
1262 | 12 | 12 | ||
1263 | 13 | from softwarecenter.enums import (SOFTWARE_CENTER_NAME_KEYRING, | ||
1264 | 14 | SOFTWARE_CENTER_SSO_DESCRIPTION, | ||
1265 | 15 | ) | ||
1266 | 16 | from softwarecenter.paths import SOFTWARE_CENTER_CACHE_DIR | 13 | from softwarecenter.paths import SOFTWARE_CENTER_CACHE_DIR |
1267 | 17 | from softwarecenter.backend.piston.scaclient import SoftwareCenterAgentAPI | 14 | from softwarecenter.backend.piston.scaclient import SoftwareCenterAgentAPI |
1268 | 18 | from softwarecenter.backend.login_sso import get_sso_backend | ||
1269 | 19 | from softwarecenter.backend.restfulclient import UbuntuSSOAPI | ||
1270 | 20 | from softwarecenter.utils import clear_token_from_ubuntu_sso | ||
1271 | 21 | 15 | ||
1272 | 22 | from gettext import gettext as _ | 16 | from gettext import gettext as _ |
1273 | 23 | 17 | ||
1274 | 24 | LOG = logging.getLogger(__name__) | 18 | LOG = logging.getLogger(__name__) |
1275 | 25 | 19 | ||
1321 | 26 | class SSOLoginHelper(object): | 20 | from softwarecenter.backend.piston.sso_helper import SSOLoginHelper |
1277 | 27 | def __init__(self, xid=0): | ||
1278 | 28 | self.oauth = None | ||
1279 | 29 | self.xid = xid | ||
1280 | 30 | self.loop = GObject.MainLoop(GObject.main_context_default()) | ||
1281 | 31 | |||
1282 | 32 | def _login_successful(self, sso_backend, oauth_result): | ||
1283 | 33 | LOG.debug("_login_successful") | ||
1284 | 34 | self.oauth = oauth_result | ||
1285 | 35 | # FIXME: actually verify the token against ubuntu SSO | ||
1286 | 36 | self.loop.quit() | ||
1287 | 37 | |||
1288 | 38 | def verify_token(self, token): | ||
1289 | 39 | LOG.debug("verify_token") | ||
1290 | 40 | def _whoami_done(sso, me): | ||
1291 | 41 | self._whoami = me | ||
1292 | 42 | self.loop.quit() | ||
1293 | 43 | self._whoami = None | ||
1294 | 44 | sso = UbuntuSSOAPI(token) | ||
1295 | 45 | sso.connect("whoami", _whoami_done) | ||
1296 | 46 | sso.connect("error", lambda sso, err: self.loop.quit()) | ||
1297 | 47 | sso.whoami() | ||
1298 | 48 | self.loop.run() | ||
1299 | 49 | LOG.debug("verify_token finished") | ||
1300 | 50 | # check if the token is valid | ||
1301 | 51 | if self._whoami is None: | ||
1302 | 52 | return False | ||
1303 | 53 | else: | ||
1304 | 54 | return True | ||
1305 | 55 | |||
1306 | 56 | def clear_token(self): | ||
1307 | 57 | clear_token_from_ubuntu_sso(SOFTWARE_CENTER_NAME_KEYRING) | ||
1308 | 58 | |||
1309 | 59 | def get_oauth_token_sync(self): | ||
1310 | 60 | self.oauth = None | ||
1311 | 61 | sso = get_sso_backend( | ||
1312 | 62 | self.xid, | ||
1313 | 63 | SOFTWARE_CENTER_NAME_KEYRING, | ||
1314 | 64 | _(SOFTWARE_CENTER_SSO_DESCRIPTION)) | ||
1315 | 65 | sso.connect("login-successful", self._login_successful) | ||
1316 | 66 | sso.connect("login-failed", lambda s: self.loop.quit()) | ||
1317 | 67 | sso.connect("login-canceled", lambda s: self.loop.quit()) | ||
1318 | 68 | sso.login_or_register() | ||
1319 | 69 | self.loop.run() | ||
1320 | 70 | return self.oauth | ||
1322 | 71 | 21 | ||
1323 | 72 | if __name__ == "__main__": | 22 | if __name__ == "__main__": |
1324 | 73 | logging.basicConfig() | 23 | logging.basicConfig() |
1325 | @@ -118,12 +68,7 @@ | |||
1326 | 118 | # check if auth is required | 68 | # check if auth is required |
1327 | 119 | if args.command in ("available_apps_qa", "subscriptions_for_me"): | 69 | if args.command in ("available_apps_qa", "subscriptions_for_me"): |
1328 | 120 | helper = SSOLoginHelper(args.parent_xid) | 70 | helper = SSOLoginHelper(args.parent_xid) |
1335 | 121 | token = helper.get_oauth_token_sync() | 71 | token = helper.get_oauth_token_and_verify_sync() |
1330 | 122 | # check if the token is valid and reset it if it is not | ||
1331 | 123 | if token and not helper.verify_token(token): | ||
1332 | 124 | helper.clear_token() | ||
1333 | 125 | # re-trigger login | ||
1334 | 126 | token = helper.get_oauth_token_sync() | ||
1336 | 127 | # if we don't have a token, error here | 72 | # if we don't have a token, error here |
1337 | 128 | if not token: | 73 | if not token: |
1338 | 129 | sys.stderr.write("ERROR: can not obtain a oauth token\n") | 74 | sys.stderr.write("ERROR: can not obtain a oauth token\n") |
1339 | 130 | 75 | ||
1340 | === removed file 'utils/piston-helpers/piston_get_useful_votes_helper.py' | |||
1341 | --- utils/piston-helpers/piston_get_useful_votes_helper.py 2011-08-19 11:25:58 +0000 | |||
1342 | +++ utils/piston-helpers/piston_get_useful_votes_helper.py 1970-01-01 00:00:00 +0000 | |||
1343 | @@ -1,46 +0,0 @@ | |||
1344 | 1 | #!/usr/bin/python | ||
1345 | 2 | |||
1346 | 3 | import pickle | ||
1347 | 4 | import logging | ||
1348 | 5 | import sys | ||
1349 | 6 | |||
1350 | 7 | from optparse import OptionParser | ||
1351 | 8 | from softwarecenter.backend.piston.rnrclient import RatingsAndReviewsAPI | ||
1352 | 9 | from piston_mini_client import APIError | ||
1353 | 10 | |||
1354 | 11 | LOG = logging.getLogger(__name__) | ||
1355 | 12 | |||
1356 | 13 | if __name__ == "__main__": | ||
1357 | 14 | logging.basicConfig() | ||
1358 | 15 | |||
1359 | 16 | # common options for optparse go here | ||
1360 | 17 | parser = OptionParser() | ||
1361 | 18 | |||
1362 | 19 | # check options | ||
1363 | 20 | parser.add_option("--username", default=None) | ||
1364 | 21 | (options, args) = parser.parse_args() | ||
1365 | 22 | |||
1366 | 23 | rnrclient = RatingsAndReviewsAPI() | ||
1367 | 24 | |||
1368 | 25 | useful_votes = [] | ||
1369 | 26 | |||
1370 | 27 | if options.username: | ||
1371 | 28 | try: | ||
1372 | 29 | useful_votes = rnrclient.get_usefulness(username=options.username) | ||
1373 | 30 | except ValueError as e: | ||
1374 | 31 | LOG.error("failed to parse '%s'" % e.doc) | ||
1375 | 32 | except APIError, e: | ||
1376 | 33 | LOG.warn("_get_useful_votes_helper: no usefulness able to be retrieved for username: %s" % (options.username)) | ||
1377 | 34 | LOG.debug("_get_reviews_threaded: no reviews able to be retrieved: %s" % e) | ||
1378 | 35 | except: | ||
1379 | 36 | LOG.exception("_get_useful_votes_helper") | ||
1380 | 37 | sys.exit(1) | ||
1381 | 38 | |||
1382 | 39 | # print to stdout where its consumed by the parent | ||
1383 | 40 | try: | ||
1384 | 41 | print pickle.dumps(useful_votes) | ||
1385 | 42 | except IOError: | ||
1386 | 43 | # this can happen if the parent gets killed, no need to trigger | ||
1387 | 44 | # apport for this | ||
1388 | 45 | pass | ||
1389 | 46 | |||
1390 | 47 | 0 | ||
1391 | === modified file 'utils/submit_review_gtk3.py' | |||
1392 | --- utils/submit_review_gtk3.py 2011-10-24 07:49:37 +0000 | |||
1393 | +++ utils/submit_review_gtk3.py 2012-01-05 15:35:32 +0000 | |||
1394 | @@ -50,7 +50,7 @@ | |||
1395 | 50 | from gettext import gettext as _ | 50 | from gettext import gettext as _ |
1396 | 51 | from optparse import OptionParser | 51 | from optparse import OptionParser |
1397 | 52 | 52 | ||
1399 | 53 | from softwarecenter.backend.restfulclient import get_ubuntu_sso_backend | 53 | from softwarecenter.backend.ubuntusso import get_ubuntu_sso_backend |
1400 | 54 | 54 | ||
1401 | 55 | import piston_mini_client | 55 | import piston_mini_client |
1402 | 56 | 56 | ||
1403 | @@ -490,6 +490,8 @@ | |||
1404 | 490 | self.ssoapi = get_ubuntu_sso_backend(self.token) | 490 | self.ssoapi = get_ubuntu_sso_backend(self.token) |
1405 | 491 | self.ssoapi.connect("whoami", self._whoami_done) | 491 | self.ssoapi.connect("whoami", self._whoami_done) |
1406 | 492 | self.ssoapi.connect("error", self._whoami_error) | 492 | self.ssoapi.connect("error", self._whoami_error) |
1407 | 493 | # this will automatically verify the token and retrigger login | ||
1408 | 494 | # if its expired | ||
1409 | 493 | self.ssoapi.whoami() | 495 | self.ssoapi.whoami() |
1410 | 494 | 496 | ||
1411 | 495 | def _whoami_done(self, ssologin, result): | 497 | def _whoami_done(self, ssologin, result): |
1412 | @@ -500,22 +502,6 @@ | |||
1413 | 500 | 502 | ||
1414 | 501 | def _whoami_error(self, ssologin, e): | 503 | def _whoami_error(self, ssologin, e): |
1415 | 502 | logging.error("whoami error '%s'" % e) | 504 | logging.error("whoami error '%s'" % e) |
1416 | 503 | # HACK: clear the token from the keyring assuming that it expired | ||
1417 | 504 | # or got deauthorized by the user on the website | ||
1418 | 505 | # this really should be done by ubuntu-sso-client itself | ||
1419 | 506 | import lazr.restfulclient.errors | ||
1420 | 507 | # compat with maverick, it does not have Unauthorized yet | ||
1421 | 508 | if hasattr(lazr.restfulclient.errors, "Unauthorized"): | ||
1422 | 509 | errortype = lazr.restfulclient.errors.Unauthorized | ||
1423 | 510 | else: | ||
1424 | 511 | errortype = lazr.restfulclient.errors.HTTPError | ||
1425 | 512 | if (type(e) == errortype and | ||
1426 | 513 | self._whoami_token_reset_nr == 0): | ||
1427 | 514 | logging.warn("authentication error, reseting token and retrying") | ||
1428 | 515 | clear_token_from_ubuntu_sso(self.appname) | ||
1429 | 516 | self._whoami_token_reset_nr += 1 | ||
1430 | 517 | self.login(show_register=False) | ||
1431 | 518 | return | ||
1432 | 519 | # show error | 505 | # show error |
1433 | 520 | self.status_spinner.hide() | 506 | self.status_spinner.hide() |
1434 | 521 | self.login_status_label.set_markup('<b><big>%s</big></b>' % _("Failed to log in")) | 507 | self.login_status_label.set_markup('<b><big>%s</big></b>' % _("Failed to log in")) |
Hey Michael! Please double check one thing, there's a funky rename in the last rev of the branch:
utils/ piston- helpers/ piston_ get_scagent_ available_ apps.py => utils/piston- helpers/ piston_ get_scagent_ available_ apps.py. THIS
There are also a few simple pyflakes fixes that we'll want to do before merging.
This is a very nice kickoff for the client-side recommendations work, thanks!!