Merge lp:~gary-lasker/software-center/recommendations-opt-out into lp:software-center

Proposed by Gary Lasker
Status: Merged
Merged at revision: 2880
Proposed branch: lp:~gary-lasker/software-center/recommendations-opt-out
Merge into: lp:software-center
Diff against target: 324 lines (+125/-25)
6 files modified
data/ui/gtk3/SoftwareCenter.ui (+16/-6)
softwarecenter/ui/gtk3/app.py (+36/-7)
softwarecenter/ui/gtk3/views/catview_gtk.py (+1/-1)
softwarecenter/ui/gtk3/widgets/containers.py (+4/-0)
softwarecenter/ui/gtk3/widgets/recommendations.py (+54/-9)
test/gtk3/test_recommendations_widgets.py (+14/-2)
To merge this branch: bzr merge lp:~gary-lasker/software-center/recommendations-opt-out
Reviewer Review Type Date Requested Status
software-store-developers Pending
Review via email: mp+97928@code.launchpad.net

Description of the change

This branch implements the recommendations opt-out feature as specified in the most recent Software Center spec from mpt (https://wiki.ubuntu.com/SoftwareCenter/Recommendations#Opting_in_and_out). It adds a new menu item to the "View" menu that reads "Turn Off Recommendations" for the case where the user has previously opted in.

In the case where the user has not yet opted in, this menu item reads "Turn On Recommendations...", and if activated it displays a dialog to show the disclaimer for uploading of data (that same text that is shown in the panel below).

Unfortunately, this branch is light on tests for the small amout of new stuff, but in actuality there is not much new code for which tests will be terribly important. The "heavy" code is already tested -- this is really just some small UI additions. Nevertheless, I apologize for that, and a follow-on branch is forthcoming that will improve this coverage. In the interest of getting this last remaining important piece of recommender functionality merged in time for it to be covered under our current outstanding string freeze/UI freeze exception bug 956779, I'm submitting the MP now.

Thanks!

To post a comment you must log in.
2858. By Gary Lasker

trunkify

2859. By Gary Lasker

make a new class RecommendationsOptInDialog in the recommendations module and use it

2860. By Gary Lasker

add new tests so that all of the recommendations widget panels are tested

Revision history for this message
Gary Lasker (gary-lasker) wrote :

Just a note that I factored the opt-in dialog code to its own class and added some new unit tests so that all of the recommendations panel widget types are tested now.

Revision history for this message
Michael Vogt (mvo) wrote :

Thanks for your branch. This is fine but I did some fixes in lp:~mvo/software-center/recommendations-opt-out/
 please double check them and merge back.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/ui/gtk3/SoftwareCenter.ui'
2--- data/ui/gtk3/SoftwareCenter.ui 2011-11-29 18:52:13 +0000
3+++ data/ui/gtk3/SoftwareCenter.ui 2012-03-19 01:17:20 +0000
4@@ -158,12 +158,6 @@
5 </object>
6 </child>
7 <child>
8- <object class="GtkSeparatorMenuItem" id="separator_login">
9- <property name="visible">True</property>
10- <property name="can_focus">False</property>
11- </object>
12- </child>
13- <child>
14 <object class="GtkMenuItem" id="menuitem_reinstall_purchases">
15 <property name="visible">True</property>
16 <property name="can_focus">False</property>
17@@ -454,6 +448,22 @@
18 <signal name="toggled" handler="on_menuitem_add_to_launcher_toggled" swapped="no"/>
19 </object>
20 </child>
21+ <child>
22+ <object class="GtkSeparatorMenuItem" id="separator_recommendations_opt_in">
23+ <property name="visible">True</property>
24+ <property name="can_focus">False</property>
25+ </object>
26+ </child>
27+ <child>
28+ <object class="GtkMenuItem" id="menuitem_recommendations">
29+ <property name="visible">True</property>
30+ <property name="can_focus">False</property>
31+ <property name="use_action_appearance">False</property>
32+ <property name="label" translatable="yes">Turn On Recommendations...</property>
33+ <property name="use_underline">True</property>
34+ <signal name="activate" handler="on_menuitem_recommendations_activate" swapped="no"/>
35+ </object>
36+ </child>
37 </object>
38 </child>
39 </object>
40
41=== modified file 'softwarecenter/ui/gtk3/app.py'
42--- softwarecenter/ui/gtk3/app.py 2012-03-16 17:49:04 +0000
43+++ softwarecenter/ui/gtk3/app.py 2012-03-19 01:17:20 +0000
44@@ -89,6 +89,8 @@
45 get_appmanager)
46 from softwarecenter.ui.gtk3.session.viewmanager import (
47 ViewManager, get_viewmanager)
48+from softwarecenter.ui.gtk3.widgets.recommendations import (
49+ RecommendationsOptInDialog)
50
51 from softwarecenter.config import get_config
52 from softwarecenter.backend import get_install_backend
53@@ -370,14 +372,14 @@
54
55 # Adapt menu entries
56 supported_menuitem = self.builder.get_object(
57- "menuitem_view_supported_only")
58+ "menuitem_view_supported_only")
59 supported_menuitem.set_label(self.distro.get_supported_filter_name())
60 file_menu = self.builder.get_object("menu1")
61
62 if not self.distro.DEVELOPER_URL:
63 help_menu = self.builder.get_object("menu_help")
64 developer_separator = self.builder.get_object(
65- "separator_developer")
66+ "separator_developer")
67 help_menu.remove(developer_separator)
68 developer_menuitem = self.builder.get_object("menuitem_developer")
69 help_menu.remove(developer_menuitem)
70@@ -495,6 +497,9 @@
71
72 def on_available_pane_created(self, widget):
73 self.available_pane.searchentry.grab_focus()
74+ rec_panel = self.available_pane.cat_view.recommended_for_you_panel
75+ self._update_recommendations_menuitem(
76+ opted_in=rec_panel.recommender_agent.is_opted_in())
77 # connect a signal to monitor the recommendations opt-in state and
78 # persist the recommendations uuid on an opt-in
79 self.available_pane.cat_view.recommended_for_you_panel.connect(
80@@ -507,13 +512,25 @@
81 #~ def on_installed_pane_created(self, widget):
82 #~ pass
83
84- def _on_recommendations_opt_in(self, agent, recommender_uuid):
85+ def _on_recommendations_opt_in(self, rec_panel, recommender_uuid):
86 self.recommender_uuid = recommender_uuid
87+ self._update_recommendations_menuitem(opted_in=True)
88
89- def _on_recommendations_opt_out(self):
90+ def _on_recommendations_opt_out(self, rec_panel):
91 # if the user opts back out of the recommender service, we
92- # reset the UUID to indicate it
93+ # reset the recommender UUID to indicate it
94 self.recommender_uuid = ""
95+ self._update_recommendations_menuitem(opted_in=False)
96+
97+ def _update_recommendations_menuitem(self, opted_in):
98+ recommendations_menuitem = self.builder.get_object(
99+ "menuitem_recommendations")
100+ if opted_in:
101+ recommendations_menuitem.set_label(
102+ _(u"Turn Off Recommendations"))
103+ else:
104+ recommendations_menuitem.set_label(
105+ _(u"Turn On Recommendations..."))
106
107 def _on_update_software_center_agent_finished(self, pid, condition):
108 LOG.info("software-center-agent finished with status %i" %
109@@ -764,7 +781,19 @@
110 from softwarecenter.backend.scagent import SoftwareCenterAgent
111 self.scagent = SoftwareCenterAgent()
112 self.scagent.connect("available-for-me",
113- self._available_for_me_result)
114+ self._available_for_me_result)
115+
116+ def on_menuitem_recommendations_activate(self, menu_item):
117+ rec_panel = self.available_pane.cat_view.recommended_for_you_panel
118+ if rec_panel.recommender_agent.is_opted_in():
119+ rec_panel.opt_out_of_recommendations_service()
120+ else:
121+ # build and show the opt-in dialog
122+ opt_in_dialog = RecommendationsOptInDialog(self.icons)
123+ res = opt_in_dialog.run()
124+ opt_in_dialog.destroy()
125+ if res == Gtk.ResponseType.YES:
126+ rec_panel.opt_in_to_recommendations_service()
127
128 def on_menuitem_reinstall_purchases_activate(self, menuitem):
129 self.view_manager.set_active_view(ViewPages.AVAILABLE)
130@@ -773,7 +802,7 @@
131 if self.available_for_me_query:
132 # we already have the list of available items, so just show it
133 self.available_pane.on_previous_purchases_activated(
134- self.available_for_me_query)
135+ self.available_for_me_query)
136 else:
137 # fetch the list of available items and show it
138 self._create_scagent_if_needed()
139
140=== modified file 'softwarecenter/ui/gtk3/views/catview_gtk.py'
141--- softwarecenter/ui/gtk3/views/catview_gtk.py 2012-03-15 21:19:02 +0000
142+++ softwarecenter/ui/gtk3/views/catview_gtk.py 2012-03-19 01:17:20 +0000
143@@ -579,7 +579,7 @@
144 self.vbox.remove(self.recommended_for_you_in_cat)
145 self.recommended_for_you_in_cat = RecommendationsPanelCategory(
146 self,
147- category)
148+ category)
149 # only show the panel in the categories view when the user
150 # is opted in to the recommender service
151 # FIXME: this is needed vs. a simple hide() on the widget because
152
153=== modified file 'softwarecenter/ui/gtk3/widgets/containers.py'
154--- softwarecenter/ui/gtk3/widgets/containers.py 2012-03-12 12:43:52 +0000
155+++ softwarecenter/ui/gtk3/widgets/containers.py 2012-03-19 01:17:20 +0000
156@@ -564,6 +564,10 @@
157 self.more = MoreLink()
158 self.header.pack_end(self.more, False, False, 0)
159
160+ def remove_more_button(self):
161+ if hasattr(self, "more"):
162+ self.header.remove(self.more)
163+
164 def render_header(self, cr, a, border_radius, assets):
165
166 if hasattr(self, "more"):
167
168=== modified file 'softwarecenter/ui/gtk3/widgets/recommendations.py'
169--- softwarecenter/ui/gtk3/widgets/recommendations.py 2012-03-15 22:23:21 +0000
170+++ softwarecenter/ui/gtk3/widgets/recommendations.py 2012-03-19 01:17:20 +0000
171@@ -91,7 +91,7 @@
172 # add the new stuff
173 self.recommended_for_you_content = FlowableGrid()
174 self.add(self.recommended_for_you_content)
175- self.spinner_notebook.show_spinner(_("Receiving recommendations…"))
176+ self.spinner_notebook.show_spinner(_(u"Receiving recommendations…"))
177 # get the recommendations from the recommender agent
178 self.recommended_for_you_cat = RecommendedForYouCategory(
179 subcategory=self.subcategory)
180@@ -136,6 +136,12 @@
181 ),
182 }
183
184+ TURN_ON_RECOMMENDATIONS_TEXT = _(u"Turn On Recommendations")
185+ RECOMMENDATIONS_OPT_IN_TEXT = _(u"To make recommendations, "
186+ "Ubuntu Software Center "
187+ "will occasionally send to Canonical an anonymous list "
188+ "of software currently installed.")
189+
190 def __init__(self, catview):
191 RecommendationsPanel.__init__(self, catview)
192 self.subcategory = None
193@@ -156,7 +162,7 @@
194 self.add(self.recommended_for_you_content)
195
196 # opt in button
197- button = Gtk.Button(_("Turn On Recommendations"))
198+ button = Gtk.Button(self.TURN_ON_RECOMMENDATIONS_TEXT)
199 button.connect("clicked", self._on_opt_in_button_clicked)
200 hbox = Gtk.Box(Gtk.Orientation.HORIZONTAL)
201 hbox.pack_start(button, False, False, 0)
202@@ -164,15 +170,16 @@
203 self.opt_in_button = button # for tests
204
205 # opt in text
206- text = _("To make recommendations, Ubuntu Software Center "
207- "will occasionally send to Canonical an anonymous list "
208- "of software currently installed.")
209+ text = self.RECOMMENDATIONS_OPT_IN_TEXT
210 label = Gtk.Label(text)
211 label.set_alignment(0, 0.5)
212 label.set_line_wrap(True)
213 vbox.pack_start(label, False, False, 0)
214
215 def _on_opt_in_button_clicked(self, button):
216+ self.opt_in_to_recommendations_service()
217+
218+ def opt_in_to_recommendations_service(self):
219 # we upload the user profile here, and only after this is finished
220 # do we fire the request for recommendations and finally display
221 # them here -- a spinner is shown for this process (the spec
222@@ -180,6 +187,14 @@
223 # progress info)
224 self._upload_user_profile_and_get_recommendations()
225
226+ def opt_out_of_recommendations_service(self):
227+ if self.recommended_for_you_content:
228+ self.recommended_for_you_content.destroy()
229+ self._show_opt_in_view()
230+ self.remove_more_button()
231+ self.show_all()
232+ self.emit("recommendations-opt-out")
233+
234 def _upload_user_profile_and_get_recommendations(self):
235 # initiate upload of the user profile here
236 self._upload_user_profile()
237@@ -256,18 +271,42 @@
238 self.hide()
239
240
241+class RecommendationsOptInDialog(Gtk.MessageDialog):
242+ """
243+ Dialog to display the recommendations opt-in message when opt-in is
244+ initiated from the menu.
245+ """
246+ def __init__(self, icons):
247+ Gtk.MessageDialog.__init__(self, flags=Gtk.DialogFlags.MODAL,
248+ type=Gtk.MessageType.INFO)
249+ self.set_title("")
250+ icon_name = "softwarecenter"
251+ if icons.has_icon(icon_name):
252+ icon = Gtk.Image.new_from_icon_name(icon_name,
253+ Gtk.IconSize.DIALOG)
254+ self.set_image(icon)
255+ icon.show()
256+ self.format_secondary_text(
257+ RecommendationsPanelLobby.RECOMMENDATIONS_OPT_IN_TEXT)
258+ self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
259+ self.add_button(RecommendationsPanelLobby.TURN_ON_RECOMMENDATIONS_TEXT,
260+ Gtk.ResponseType.YES)
261+ self.set_default_response(Gtk.ResponseType.YES)
262+
263+
264 # test helpers
265-def get_test_window():
266+def get_test_window(panel_type="lobby"):
267 import softwarecenter.log
268 softwarecenter.log.root.setLevel(level=logging.DEBUG)
269 fmt = logging.Formatter("%(name)s - %(message)s", None)
270 softwarecenter.log.handler.setFormatter(fmt)
271
272- # this is *way* to complicated we should *not* need a CatView
273+ # this is *way* too complicated we should *not* need a CatView
274 # here! see FIXME in RecommendationsPanel.__init__()
275 from softwarecenter.ui.gtk3.views.catview_gtk import CategoriesViewGtk
276 from softwarecenter.testutils import (
277- get_test_db, get_test_pkg_info, get_test_gtk3_icon_cache)
278+ get_test_db, get_test_pkg_info, get_test_gtk3_icon_cache,
279+ get_test_categories)
280 cache = get_test_pkg_info()
281 db = get_test_db()
282 icons = get_test_gtk3_icon_cache()
283@@ -277,7 +316,13 @@
284 db,
285 icons)
286
287- view = RecommendationsPanelLobby(catview)
288+ if panel_type is "lobby":
289+ view = RecommendationsPanelLobby(catview)
290+ elif panel_type is "category":
291+ cats = get_test_categories(db)
292+ view = RecommendationsPanelCategory(catview, cats[0])
293+ else: # panel_type is "details":
294+ view = RecommendationsPanelDetails(catview)
295
296 win = Gtk.Window()
297 win.connect("destroy", lambda x: Gtk.main_quit())
298
299=== modified file 'test/gtk3/test_recommendations_widgets.py'
300--- test/gtk3/test_recommendations_widgets.py 2012-03-01 11:29:55 +0000
301+++ test/gtk3/test_recommendations_widgets.py 2012-03-19 01:17:20 +0000
302@@ -16,8 +16,20 @@
303
304 class TestRecommendationsWidgets(unittest.TestCase):
305
306- def test_recommendations_widgets(self):
307- win = get_test_window()
308+ def test_recommendations_lobby(self):
309+ win = get_test_window(panel_type="lobby")
310+ win.show_all()
311+ GObject.timeout_add(TIMEOUT, lambda: win.destroy())
312+ Gtk.main()
313+
314+ def test_recommendations_category(self):
315+ win = get_test_window(panel_type="category")
316+ win.show_all()
317+ GObject.timeout_add(TIMEOUT, lambda: win.destroy())
318+ Gtk.main()
319+
320+ def test_recommendations_details(self):
321+ win = get_test_window(panel_type="details")
322 win.show_all()
323 GObject.timeout_add(TIMEOUT, lambda: win.destroy())
324 Gtk.main()

Subscribers

People subscribed via source and target branches