Merge lp:~gary-lasker/software-center/recommender-profile-uploads-lp944693 into lp:software-center
- recommender-profile-uploads-lp944693
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 2939 |
Proposed branch: | lp:~gary-lasker/software-center/recommender-profile-uploads-lp944693 |
Merge into: | lp:software-center |
Diff against target: |
382 lines (+120/-65) 4 files modified
softwarecenter/backend/recagent.py (+73/-35) softwarecenter/ui/gtk3/app.py (+23/-13) softwarecenter/ui/gtk3/widgets/recommendations.py (+21/-12) test/test_recagent.py (+3/-5) |
To merge this branch: | bzr merge lp:~gary-lasker/software-center/recommender-profile-uploads-lp944693 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Vogt | Approve | ||
Review via email: mp+100360@code.launchpad.net |
Commit message
Description of the change
This branch implements the fix for bug 944693. A timed check is added that is set to run 45 seconds after each startup of Software Center (this purpose of the delay is to avoid impacting startup time). The current profile (list of installed apps) is compared to the previous uploaded profile, and if they differ the new profile data is uploaded to the recommender service. If the profile is found to be unchanged since the previous upload, the server upload is skipped.
- 2920. By Gary Lasker
-
update unit test for the changed submit-
profile- finished call
Gary Lasker (gary-lasker) wrote : | # |
Thanks Michael, that sounds great! I definitely also prefer we only use a single agent object instance, even if we don't enforce it as a singleton (which really should not be necessary). I appreciate your help with this and look forward to seeing your branch!
- 2921. By Gary Lasker
-
merged lp:~mvo/software-center/recagent-almost-singleton, very nice improvements, many thanks Michael
Preview Diff
1 | === modified file 'softwarecenter/backend/recagent.py' | |||
2 | --- softwarecenter/backend/recagent.py 2012-03-19 17:08:56 +0000 | |||
3 | +++ softwarecenter/backend/recagent.py 2012-04-02 16:58:19 +0000 | |||
4 | @@ -21,6 +21,7 @@ | |||
5 | 21 | 21 | ||
6 | 22 | from gi.repository import GObject | 22 | from gi.repository import GObject |
7 | 23 | import logging | 23 | import logging |
8 | 24 | import hashlib | ||
9 | 24 | 25 | ||
10 | 25 | import softwarecenter.paths | 26 | import softwarecenter.paths |
11 | 26 | from spawn_helper import SpawnHelper | 27 | from spawn_helper import SpawnHelper |
12 | @@ -45,7 +46,7 @@ | |||
13 | 45 | ), | 46 | ), |
14 | 46 | "submit-profile-finished": (GObject.SIGNAL_RUN_LAST, | 47 | "submit-profile-finished": (GObject.SIGNAL_RUN_LAST, |
15 | 47 | GObject.TYPE_NONE, | 48 | GObject.TYPE_NONE, |
17 | 48 | (GObject.TYPE_PYOBJECT, str), | 49 | (GObject.TYPE_PYOBJECT,), |
18 | 49 | ), | 50 | ), |
19 | 50 | "submit-anon-profile-finished": (GObject.SIGNAL_RUN_LAST, | 51 | "submit-anon-profile-finished": (GObject.SIGNAL_RUN_LAST, |
20 | 51 | GObject.TYPE_NONE, | 52 | GObject.TYPE_NONE, |
21 | @@ -76,7 +77,6 @@ | |||
22 | 76 | def __init__(self, xid=None): | 77 | def __init__(self, xid=None): |
23 | 77 | GObject.GObject.__init__(self) | 78 | GObject.GObject.__init__(self) |
24 | 78 | self.xid = xid | 79 | self.xid = xid |
25 | 79 | self.recommender_uuid = self._get_recommender_uuid() | ||
26 | 80 | 80 | ||
27 | 81 | def query_server_status(self): | 81 | def query_server_status(self): |
28 | 82 | # build the command | 82 | # build the command |
29 | @@ -88,29 +88,76 @@ | |||
30 | 88 | spawner.run_generic_piston_helper( | 88 | spawner.run_generic_piston_helper( |
31 | 89 | "SoftwareCenterRecommenderAPI", "server_status") | 89 | "SoftwareCenterRecommenderAPI", "server_status") |
32 | 90 | 90 | ||
33 | 91 | def _calc_profile_id(self, profile): | ||
34 | 92 | """ Return a profile id (md5 hash of a profile) for the given profile | ||
35 | 93 | """ | ||
36 | 94 | return hashlib.md5(str(profile)).hexdigest() | ||
37 | 95 | |||
38 | 96 | @property | ||
39 | 97 | def recommender_uuid(self): | ||
40 | 98 | config = get_config() | ||
41 | 99 | if config.has_option("general", "recommender_uuid"): | ||
42 | 100 | recommender_uuid = config.get("general", | ||
43 | 101 | "recommender_uuid") | ||
44 | 102 | else: | ||
45 | 103 | recommender_uuid = "" | ||
46 | 104 | return recommender_uuid | ||
47 | 105 | |||
48 | 106 | @property | ||
49 | 107 | def recommender_profile_id(self): | ||
50 | 108 | config = get_config() | ||
51 | 109 | if config.has_option("general", "recommender_profile_id"): | ||
52 | 110 | recommender_profile_id = config.get("general", | ||
53 | 111 | "recommender_profile_id") | ||
54 | 112 | else: | ||
55 | 113 | recommender_profile_id = "" | ||
56 | 114 | return recommender_profile_id | ||
57 | 115 | |||
58 | 116 | def _set_recommender_profile_id(self, profile_id): | ||
59 | 117 | config = get_config() | ||
60 | 118 | if not config.has_section("general"): | ||
61 | 119 | config.add_section("general") | ||
62 | 120 | config.set("general", "recommender_profile_id", profile_id) | ||
63 | 121 | |||
64 | 122 | def _set_recommender_uuid(self, uuid): | ||
65 | 123 | config = get_config() | ||
66 | 124 | if not config.has_section("general"): | ||
67 | 125 | config.add_section("general") | ||
68 | 126 | config.set("general", "recommender_uuid", uuid) | ||
69 | 127 | |||
70 | 91 | def post_submit_profile(self, db): | 128 | def post_submit_profile(self, db): |
71 | 92 | """ This will post the users profile to the recommender server | 129 | """ This will post the users profile to the recommender server |
72 | 93 | and also generate the UUID for the user if that is not | 130 | and also generate the UUID for the user if that is not |
73 | 94 | there yet | 131 | there yet |
74 | 95 | """ | 132 | """ |
79 | 96 | # if we have not already set a recommender UUID, now is the time | 133 | recommender_uuid = self.recommender_uuid |
80 | 97 | # to do it | 134 | if not recommender_uuid: |
81 | 98 | if not self.recommender_uuid: | 135 | # generate a new uuid, but do not save it yet, this will |
82 | 99 | self.recommender_uuid = get_uuid() | 136 | # be done later in _on_submit_profile_data |
83 | 137 | recommender_uuid = get_uuid() | ||
84 | 100 | installed_pkglist = [app.pkgname | 138 | installed_pkglist = [app.pkgname |
85 | 101 | for app in get_installed_apps_list(db)] | 139 | for app in get_installed_apps_list(db)] |
98 | 102 | data = self._generate_submit_profile_data(self.recommender_uuid, | 140 | profile = self._generate_submit_profile_data(recommender_uuid, |
99 | 103 | installed_pkglist) | 141 | installed_pkglist) |
100 | 104 | # build the command | 142 | |
101 | 105 | spawner = SpawnHelper() | 143 | # compare profiles to see if there has been a change, and if there |
102 | 106 | spawner.parent_xid = self.xid | 144 | # has, do the profile update |
103 | 107 | spawner.needs_auth = True | 145 | current_recommender_profile_id = self._calc_profile_id(profile) |
104 | 108 | spawner.connect("data-available", self._on_submit_profile_data) | 146 | if current_recommender_profile_id != self.recommender_profile_id: |
105 | 109 | spawner.connect("error", lambda spawner, err: self.emit("error", err)) | 147 | LOG.info("Submitting recommendations profile to the server") |
106 | 110 | spawner.run_generic_piston_helper( | 148 | self._set_recommender_profile_id(current_recommender_profile_id) |
107 | 111 | "SoftwareCenterRecommenderAPI", | 149 | # build the command and upload the profile |
108 | 112 | "submit_profile", | 150 | spawner = SpawnHelper() |
109 | 113 | data=data) | 151 | spawner.parent_xid = self.xid |
110 | 152 | spawner.needs_auth = True | ||
111 | 153 | spawner.connect("data-available", self._on_submit_profile_data, | ||
112 | 154 | recommender_uuid) | ||
113 | 155 | spawner.connect( | ||
114 | 156 | "error", lambda spawner, err: self.emit("error", err)) | ||
115 | 157 | spawner.run_generic_piston_helper( | ||
116 | 158 | "SoftwareCenterRecommenderAPI", | ||
117 | 159 | "submit_profile", | ||
118 | 160 | data=profile) | ||
119 | 114 | 161 | ||
120 | 115 | def post_submit_anon_profile(self, uuid, installed_packages, extra): | 162 | def post_submit_anon_profile(self, uuid, installed_packages, extra): |
121 | 116 | # build the command | 163 | # build the command |
122 | @@ -188,7 +235,9 @@ | |||
123 | 188 | return False | 235 | return False |
124 | 189 | 236 | ||
125 | 190 | def opt_out(self): | 237 | def opt_out(self): |
127 | 191 | self.recommender_uuid = "" | 238 | config = get_config() |
128 | 239 | config.set("general", "recommender_uuid", "") | ||
129 | 240 | config.set("general", "recommender_profile_id", "") | ||
130 | 192 | 241 | ||
131 | 193 | def _on_server_status_data(self, spawner, piston_server_status): | 242 | def _on_server_status_data(self, spawner, piston_server_status): |
132 | 194 | self.emit("server-status", piston_server_status) | 243 | self.emit("server-status", piston_server_status) |
133 | @@ -196,13 +245,14 @@ | |||
134 | 196 | def _on_profile_data(self, spawner, piston_profile): | 245 | def _on_profile_data(self, spawner, piston_profile): |
135 | 197 | self.emit("profile", piston_profile) | 246 | self.emit("profile", piston_profile) |
136 | 198 | 247 | ||
138 | 199 | def _on_submit_profile_data(self, spawner, piston_submit_profile): | 248 | def _on_submit_profile_data(self, spawner, piston_submit_profile, |
139 | 249 | recommender_uuid): | ||
140 | 250 | self._set_recommender_uuid(recommender_uuid) | ||
141 | 200 | self.emit("submit-profile-finished", | 251 | self.emit("submit-profile-finished", |
144 | 201 | piston_submit_profile, | 252 | piston_submit_profile) |
143 | 202 | self.recommender_uuid) | ||
145 | 203 | 253 | ||
146 | 204 | def _on_submit_anon_profile_data(self, spawner, | 254 | def _on_submit_anon_profile_data(self, spawner, |
148 | 205 | piston_submit_anon_profile): | 255 | piston_submit_anon_profile): |
149 | 206 | self.emit("submit-anon_profile", piston_submit_anon_profile) | 256 | self.emit("submit-anon_profile", piston_submit_anon_profile) |
150 | 207 | 257 | ||
151 | 208 | def _on_recommend_me_data(self, spawner, piston_me_apps): | 258 | def _on_recommend_me_data(self, spawner, piston_me_apps): |
152 | @@ -217,18 +267,6 @@ | |||
153 | 217 | def _on_recommend_top_data(self, spawner, piston_top_apps): | 267 | def _on_recommend_top_data(self, spawner, piston_top_apps): |
154 | 218 | self.emit("recommend-top", piston_top_apps) | 268 | self.emit("recommend-top", piston_top_apps) |
155 | 219 | 269 | ||
156 | 220 | def _get_recommender_uuid(self): | ||
157 | 221 | """ returns the recommender UUID value, which can be empty if it | ||
158 | 222 | has not yet been set (indicating that the user has not yet | ||
159 | 223 | opted-in to the recommender service) | ||
160 | 224 | """ | ||
161 | 225 | config = get_config() | ||
162 | 226 | if config.has_option("general", "recommender_uuid"): | ||
163 | 227 | recommender_uuid = config.get("general", "recommender_uuid") | ||
164 | 228 | if recommender_uuid: | ||
165 | 229 | return recommender_uuid | ||
166 | 230 | return "" | ||
167 | 231 | |||
168 | 232 | def _generate_submit_profile_data(self, recommender_uuid, package_list): | 270 | def _generate_submit_profile_data(self, recommender_uuid, package_list): |
169 | 233 | submit_profile_data = [{ | 271 | submit_profile_data = [{ |
170 | 234 | 'uuid': recommender_uuid, | 272 | 'uuid': recommender_uuid, |
171 | 235 | 273 | ||
172 | === modified file 'softwarecenter/ui/gtk3/app.py' | |||
173 | --- softwarecenter/ui/gtk3/app.py 2012-03-23 09:45:35 +0000 | |||
174 | +++ softwarecenter/ui/gtk3/app.py 2012-04-02 16:58:19 +0000 | |||
175 | @@ -97,6 +97,7 @@ | |||
176 | 97 | from softwarecenter.config import get_config | 97 | from softwarecenter.config import get_config |
177 | 98 | from softwarecenter.backend import get_install_backend | 98 | from softwarecenter.backend import get_install_backend |
178 | 99 | from softwarecenter.backend.login_sso import get_sso_backend | 99 | from softwarecenter.backend.login_sso import get_sso_backend |
179 | 100 | from softwarecenter.backend.recagent import RecommenderAgent | ||
180 | 100 | 101 | ||
181 | 101 | from softwarecenter.backend.channel import AllInstalledChannel | 102 | from softwarecenter.backend.channel import AllInstalledChannel |
182 | 102 | from softwarecenter.backend.reviews import get_review_loader, UsefulnessCache | 103 | from softwarecenter.backend.reviews import get_review_loader, UsefulnessCache |
183 | @@ -269,7 +270,6 @@ | |||
184 | 269 | self.scagent = None | 270 | self.scagent = None |
185 | 270 | self.sso = None | 271 | self.sso = None |
186 | 271 | self.available_for_me_query = None | 272 | self.available_for_me_query = None |
187 | 272 | self.recommender_uuid = "" | ||
188 | 273 | 273 | ||
189 | 274 | Gtk.Window.set_default_icon_name("softwarecenter") | 274 | Gtk.Window.set_default_icon_name("softwarecenter") |
190 | 275 | 275 | ||
191 | @@ -414,6 +414,10 @@ | |||
192 | 414 | # keep the cache clean | 414 | # keep the cache clean |
193 | 415 | GObject.timeout_add_seconds(15, self._run_expunge_cache_helper) | 415 | GObject.timeout_add_seconds(15, self._run_expunge_cache_helper) |
194 | 416 | 416 | ||
195 | 417 | # check to see if a new recommendations profile upload is | ||
196 | 418 | # needed and upload if necessary | ||
197 | 419 | GObject.timeout_add_seconds(45, self._upload_recommendations_profile) | ||
198 | 420 | |||
199 | 417 | # TODO: Remove the following two lines once we have remove repository | 421 | # TODO: Remove the following two lines once we have remove repository |
200 | 418 | # support in aptdaemon (see LP: #723911) | 422 | # support in aptdaemon (see LP: #723911) |
201 | 419 | self.menu_file.remove(self.menuitem_deauthorize_computer) | 423 | self.menu_file.remove(self.menuitem_deauthorize_computer) |
202 | @@ -493,9 +497,8 @@ | |||
203 | 493 | 497 | ||
204 | 494 | def on_available_pane_created(self, widget): | 498 | def on_available_pane_created(self, widget): |
205 | 495 | self.available_pane.searchentry.grab_focus() | 499 | self.available_pane.searchentry.grab_focus() |
206 | 496 | rec_panel = self.available_pane.cat_view.recommended_for_you_panel | ||
207 | 497 | self._update_recommendations_menuitem( | 500 | self._update_recommendations_menuitem( |
209 | 498 | opted_in=rec_panel.recommender_agent.is_opted_in()) | 501 | opted_in=self._get_recommender_agent().is_opted_in()) |
210 | 499 | # connect a signal to monitor the recommendations opt-in state and | 502 | # connect a signal to monitor the recommendations opt-in state and |
211 | 500 | # persist the recommendations uuid on an opt-in | 503 | # persist the recommendations uuid on an opt-in |
212 | 501 | self.available_pane.cat_view.recommended_for_you_panel.connect( | 504 | self.available_pane.cat_view.recommended_for_you_panel.connect( |
213 | @@ -509,14 +512,10 @@ | |||
214 | 509 | #~ def on_installed_pane_created(self, widget): | 512 | #~ def on_installed_pane_created(self, widget): |
215 | 510 | #~ pass | 513 | #~ pass |
216 | 511 | 514 | ||
219 | 512 | def _on_recommendations_opt_in(self, rec_panel, recommender_uuid): | 515 | def _on_recommendations_opt_in(self, rec_panel): |
218 | 513 | self.recommender_uuid = recommender_uuid | ||
220 | 514 | self._update_recommendations_menuitem(opted_in=True) | 516 | self._update_recommendations_menuitem(opted_in=True) |
221 | 515 | 517 | ||
222 | 516 | def _on_recommendations_opt_out(self, rec_panel): | 518 | def _on_recommendations_opt_out(self, rec_panel): |
223 | 517 | # if the user opts back out of the recommender service, we | ||
224 | 518 | # reset the recommender UUID to indicate it | ||
225 | 519 | self.recommender_uuid = "" | ||
226 | 520 | self._update_recommendations_menuitem(opted_in=False) | 519 | self._update_recommendations_menuitem(opted_in=False) |
227 | 521 | 520 | ||
228 | 522 | def _update_recommendations_menuitem(self, opted_in): | 521 | def _update_recommendations_menuitem(self, opted_in): |
229 | @@ -527,6 +526,16 @@ | |||
230 | 527 | self.menuitem_recommendations.set_label( | 526 | self.menuitem_recommendations.set_label( |
231 | 528 | _(u"Turn On Recommendations…")) | 527 | _(u"Turn On Recommendations…")) |
232 | 529 | 528 | ||
233 | 529 | def _upload_recommendations_profile(self): | ||
234 | 530 | recommender_agent = self._get_recommender_agent() | ||
235 | 531 | if recommender_agent.is_opted_in(): | ||
236 | 532 | recommender_agent.post_submit_profile(self.db) | ||
237 | 533 | |||
238 | 534 | def _get_recommender_agent(self): | ||
239 | 535 | if not hasattr(self, "_recommender_agent"): | ||
240 | 536 | self._recommender_agent = RecommenderAgent() | ||
241 | 537 | return self._recommender_agent | ||
242 | 538 | |||
243 | 530 | def _on_update_software_center_agent_finished(self, pid, condition): | 539 | def _on_update_software_center_agent_finished(self, pid, condition): |
244 | 531 | LOG.info("software-center-agent finished with status %i" % | 540 | LOG.info("software-center-agent finished with status %i" % |
245 | 532 | os.WEXITSTATUS(condition)) | 541 | os.WEXITSTATUS(condition)) |
246 | @@ -780,7 +789,7 @@ | |||
247 | 780 | 789 | ||
248 | 781 | def on_menuitem_recommendations_activate(self, menu_item): | 790 | def on_menuitem_recommendations_activate(self, menu_item): |
249 | 782 | rec_panel = self.available_pane.cat_view.recommended_for_you_panel | 791 | rec_panel = self.available_pane.cat_view.recommended_for_you_panel |
251 | 783 | if rec_panel.recommender_agent.is_opted_in(): | 792 | if self._get_recommender_agent().is_opted_in(): |
252 | 784 | rec_panel.opt_out_of_recommendations_service() | 793 | rec_panel.opt_out_of_recommendations_service() |
253 | 785 | else: | 794 | else: |
254 | 786 | # build and show the opt-in dialog | 795 | # build and show the opt-in dialog |
255 | @@ -1294,9 +1303,6 @@ | |||
256 | 1294 | else: | 1303 | else: |
257 | 1295 | # initial default state is to add to launcher, per spec | 1304 | # initial default state is to add to launcher, per spec |
258 | 1296 | self.available_pane.add_to_launcher_enabled = True | 1305 | self.available_pane.add_to_launcher_enabled = True |
259 | 1297 | if self.config.has_option("general", "recommender_uuid"): | ||
260 | 1298 | self.recommender_uuid = self.config.get("general", | ||
261 | 1299 | "recommender_uuid") | ||
262 | 1300 | 1306 | ||
263 | 1301 | def save_state(self): | 1307 | def save_state(self): |
264 | 1302 | LOG.debug("save_state") | 1308 | LOG.debug("save_state") |
265 | @@ -1318,9 +1324,13 @@ | |||
266 | 1318 | self.config.set("general", "add_to_launcher", "True") | 1324 | self.config.set("general", "add_to_launcher", "True") |
267 | 1319 | else: | 1325 | else: |
268 | 1320 | self.config.set("general", "add_to_launcher", "False") | 1326 | self.config.set("general", "add_to_launcher", "False") |
269 | 1327 | # store the recommender values | ||
270 | 1321 | self.config.set("general", | 1328 | self.config.set("general", |
271 | 1322 | "recommender_uuid", | 1329 | "recommender_uuid", |
273 | 1323 | self.recommender_uuid) | 1330 | self._get_recommender_agent().recommender_uuid) |
274 | 1331 | self.config.set("general", | ||
275 | 1332 | "recommender_profile_id", | ||
276 | 1333 | self._get_recommender_agent().recommender_profile_id) | ||
277 | 1324 | self.config.write() | 1334 | self.config.write() |
278 | 1325 | 1335 | ||
279 | 1326 | def run(self, args): | 1336 | def run(self, args): |
280 | 1327 | 1337 | ||
281 | === modified file 'softwarecenter/ui/gtk3/widgets/recommendations.py' | |||
282 | --- softwarecenter/ui/gtk3/widgets/recommendations.py 2012-03-20 10:28:51 +0000 | |||
283 | +++ softwarecenter/ui/gtk3/widgets/recommendations.py 2012-04-02 16:58:19 +0000 | |||
284 | @@ -129,7 +129,7 @@ | |||
285 | 129 | __gsignals__ = { | 129 | __gsignals__ = { |
286 | 130 | "recommendations-opt-in": (GObject.SIGNAL_RUN_LAST, | 130 | "recommendations-opt-in": (GObject.SIGNAL_RUN_LAST, |
287 | 131 | GObject.TYPE_NONE, | 131 | GObject.TYPE_NONE, |
289 | 132 | (GObject.TYPE_STRING,), | 132 | (), |
290 | 133 | ), | 133 | ), |
291 | 134 | "recommendations-opt-out": (GObject.SIGNAL_RUN_LAST, | 134 | "recommendations-opt-out": (GObject.SIGNAL_RUN_LAST, |
292 | 135 | GObject.TYPE_NONE, | 135 | GObject.TYPE_NONE, |
293 | @@ -202,13 +202,7 @@ | |||
294 | 202 | self.remove_more_button() | 202 | self.remove_more_button() |
295 | 203 | self.show_all() | 203 | self.show_all() |
296 | 204 | self.emit("recommendations-opt-out") | 204 | self.emit("recommendations-opt-out") |
304 | 205 | try: | 205 | self._disconnect_recommender_listeners() |
298 | 206 | self.recommender_agent.disconnect_by_func( | ||
299 | 207 | self._on_profile_submitted) | ||
300 | 208 | self.recommender_agent.disconnect_by_func( | ||
301 | 209 | self._on_profile_submitted_error) | ||
302 | 210 | except TypeError: | ||
303 | 211 | pass | ||
305 | 212 | 206 | ||
306 | 213 | def _upload_user_profile_and_get_recommendations(self): | 207 | def _upload_user_profile_and_get_recommendations(self): |
307 | 214 | # initiate upload of the user profile here | 208 | # initiate upload of the user profile here |
308 | @@ -222,20 +216,35 @@ | |||
309 | 222 | self._on_profile_submitted_error) | 216 | self._on_profile_submitted_error) |
310 | 223 | self.recommender_agent.post_submit_profile(self.catview.db) | 217 | self.recommender_agent.post_submit_profile(self.catview.db) |
311 | 224 | 218 | ||
313 | 225 | def _on_profile_submitted(self, agent, profile, recommender_uuid): | 219 | def _on_profile_submitted(self, agent, profile): |
314 | 226 | # after the user profile data has been uploaded, make the request | 220 | # after the user profile data has been uploaded, make the request |
315 | 227 | # and load the the recommended_for_you content | 221 | # and load the the recommended_for_you content |
319 | 228 | LOG.debug("The recommendations profile has been successfully " | 222 | LOG.debug("The updated profile was successfully submitted to the " |
320 | 229 | "submitted to the recommender agent") | 223 | "recommender service") |
321 | 230 | self.emit("recommendations-opt-in", recommender_uuid) | 224 | # only detect the very first profile upload as that indicates |
322 | 225 | # the user's initial opt-in | ||
323 | 231 | self._update_recommended_for_you_content() | 226 | self._update_recommended_for_you_content() |
324 | 227 | self._disconnect_recommender_listeners() | ||
325 | 228 | self.emit("recommendations-opt-in") | ||
326 | 232 | 229 | ||
327 | 233 | def _on_profile_submitted_error(self, agent, msg): | 230 | def _on_profile_submitted_error(self, agent, msg): |
328 | 234 | LOG.warn("Error while submitting the recommendations profile to the " | 231 | LOG.warn("Error while submitting the recommendations profile to the " |
329 | 235 | "recommender agent: %s" % msg) | 232 | "recommender agent: %s" % msg) |
330 | 236 | # TODO: handle this! display an error message in the panel | 233 | # TODO: handle this! display an error message in the panel |
331 | 234 | # detect the very first profile upload as that indicates | ||
332 | 235 | # the user's initial opt-in | ||
333 | 236 | self._disconnect_recommender_listeners() | ||
334 | 237 | self._hide_recommended_for_you_panel() | 237 | self._hide_recommended_for_you_panel() |
335 | 238 | 238 | ||
336 | 239 | def _disconnect_recommender_listeners(self): | ||
337 | 240 | try: | ||
338 | 241 | self.recommender_agent.disconnect_by_func( | ||
339 | 242 | self._on_profile_submitted) | ||
340 | 243 | self.recommender_agent.disconnect_by_func( | ||
341 | 244 | self._on_profile_submitted_error) | ||
342 | 245 | except TypeError: | ||
343 | 246 | pass | ||
344 | 247 | |||
345 | 239 | 248 | ||
346 | 240 | class RecommendationsPanelDetails(RecommendationsPanel): | 249 | class RecommendationsPanelDetails(RecommendationsPanel): |
347 | 241 | """ | 250 | """ |
348 | 242 | 251 | ||
349 | === modified file 'test/test_recagent.py' | |||
350 | --- test/test_recagent.py 2012-03-20 09:27:18 +0000 | |||
351 | +++ test/test_recagent.py 2012-04-02 16:58:19 +0000 | |||
352 | @@ -3,7 +3,6 @@ | |||
353 | 3 | from gi.repository import GObject | 3 | from gi.repository import GObject |
354 | 4 | import unittest | 4 | import unittest |
355 | 5 | import os | 5 | import os |
356 | 6 | import uuid | ||
357 | 7 | 6 | ||
358 | 8 | from mock import patch | 7 | from mock import patch |
359 | 9 | 8 | ||
360 | @@ -40,20 +39,19 @@ | |||
361 | 40 | def _patched_on_submit_profile_data(*args, **kwargs): | 39 | def _patched_on_submit_profile_data(*args, **kwargs): |
362 | 41 | piston_submit_profile = {} | 40 | piston_submit_profile = {} |
363 | 42 | recommender_agent.emit("submit-profile-finished", | 41 | recommender_agent.emit("submit-profile-finished", |
366 | 43 | piston_submit_profile, | 42 | piston_submit_profile) |
365 | 44 | uuid.uuid1()) | ||
367 | 45 | mock_spawn_helper_run.side_effect = _patched_on_submit_profile_data | 43 | mock_spawn_helper_run.side_effect = _patched_on_submit_profile_data |
368 | 46 | recommender_agent = RecommenderAgent() | 44 | recommender_agent = RecommenderAgent() |
369 | 47 | recommender_agent.connect("submit-profile-finished", self.on_query_done) | 45 | recommender_agent.connect("submit-profile-finished", self.on_query_done) |
370 | 48 | recommender_agent.connect("error", self.on_query_error) | 46 | recommender_agent.connect("error", self.on_query_error) |
371 | 47 | recommender_agent._calc_profile_id = lambda profile: "i-am-random" | ||
372 | 49 | db = get_test_db() | 48 | db = get_test_db() |
373 | 50 | recommender_agent.post_submit_profile(db) | 49 | recommender_agent.post_submit_profile(db) |
374 | 51 | self.assertFalse(self.error) | 50 | self.assertFalse(self.error) |
375 | 52 | args, kwargs = mock_spawn_helper_run.call_args | 51 | args, kwargs = mock_spawn_helper_run.call_args |
376 | 53 | self.assertNotEqual(kwargs['data'][0]['uuid'], None) | ||
377 | 54 | self.assertNotEqual(kwargs['data'][0]['package_list'], []) | 52 | self.assertNotEqual(kwargs['data'][0]['package_list'], []) |
378 | 55 | 53 | ||
380 | 56 | def on_query_done(self, recagent, data, uuid=""): | 54 | def on_query_done(self, recagent, data): |
381 | 57 | print "query done, data: '%s'" % data | 55 | print "query done, data: '%s'" % data |
382 | 58 | self.loop.quit() | 56 | self.loop.quit() |
383 | 59 | 57 |
Thanks, that looks good and works nicely!
Reading the comment about that we should use a singleton made me think a bit and I would like to
propose some tweaks where we might get away without having to use one. But I will ask you for review
on this approach once I added code for it.