diff -Nru software-center-5.1.10/data/expunge-cache.py software-center-5.1.11/data/expunge-cache.py --- software-center-5.1.10/data/expunge-cache.py 1970-01-01 00:00:00.000000000 +0000 +++ software-center-5.1.11/data/expunge-cache.py 2012-03-01 11:30:44.000000000 +0000 @@ -0,0 +1,80 @@ +#!/usr/bin/python + +""" +Expunge httplib2 caches +""" + +import argparse +import logging +import os +import time +import sys + +class ExpungeCache(object): + def __init__(self, dirs, args): + self.dirs = dirs + # days to keep data in the cache (0 == disabled) + self.keep_time = 60*60*24* args.by_days + self.keep_only_http200 = args.by_unsuccessful_http_states + self.dry_run = args.dry_run + + def _rm(self, f): + if self.dry_run: + print "Would delete: %s" % f + else: + logging.debug("Deleting: %s" % f) + os.unlink(f) + + def clean(self): + # go over the directories + now = time.time() + for d in self.dirs: + for root, dirs, files in os.walk(d): + for f in files: + fullpath = os.path.join(root, f) + header = open(fullpath).readline().strip() + if not header.startswith("status:"): + logging.debug( + "Skipping files with unknown header: '%s'" % f) + continue + if self.keep_only_http200 and header != "status: 200": + self._rm(fullpath) + if self.keep_time: + mtime = os.path.getmtime(fullpath) + logging.debug("mtime of '%s': '%s" % (f, mtime)) + if (mtime + self.keep_time) < now: + self._rm(fullpath) + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description='clean software-center httplib2 cache') + parser.add_argument( + '--debug', action="store_true", + help='show debug output') + parser.add_argument( + '--dry-run', action="store_true", + help='do not act, just show what would be done') + parser.add_argument( + 'directories', metavar='directory', nargs='+', type=str, + help='directories to be checked') + parser.add_argument( + '--by-days', type=int, default=0, + help='expire everything older than N days') + parser.add_argument( + '--by-unsuccessful-http-states', action="store_true", + help='expire any non 200 status responses') + args = parser.parse_args() + + if args.debug: + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(level=logging.INFO) + + # sanity checking + if args.by_days == 0 and not args.by_unsuccessful_http_states: + print "Need either --by-days or --by-unsuccessful-http-states argument" + sys.exit(1) + + # do it + cleaner = ExpungeCache(args.directories, args) + cleaner.clean() diff -Nru software-center-5.1.10/debian/changelog software-center-5.1.11/debian/changelog --- software-center-5.1.10/debian/changelog 2012-02-23 15:58:30.000000000 +0000 +++ software-center-5.1.11/debian/changelog 2012-03-01 19:11:27.000000000 +0000 @@ -1,3 +1,56 @@ +software-center (5.1.11) precise; urgency=low + + [ Michael Vogt ] + * softwarecenter/ui/gtk3/views/appdetailsview.py: + - only set vadj/hadj if we have the objects for it (LP: #941384) + * lp:~mvo/software-center/get-installed-apps-instead-of-pkgs: + - refactor to move the recommender UUID code into the back end, + add a mock test for the submit_profile call to the server + * lp:~mvo/software-center/region-blacklist: + - add support for filtering out apps that are blacklisted for certain + regions such that they will not appear in any searches anymore + * lp:~mvo/software-center/installed-view-speedup: + - remove an unused notebook in the installed view to speed up + opening software center with an already installed package + * softwarecenter/ui/gtk3/widgets/recommendations.py: + - fix per-app recommendations show/hide logic + * lp:~mvo/software-center/spinner-fixes: + - consolidate various instances of the spinner notebook and instead + use a single SpinnerNotebook class everywhere + * lp:~mvo/software-center/recommender-fixes: + - tiny fixes to make the recommendations-for-you actually work + with the rec.ubuntu.com server + * lp:~mvo/software-center/expunge-cache: + - add a helper that keeps the cache clean from cached 301/404 etc + responses, it also offers cleanup by mtime but that is currently + not used (only tested) + * lp:~mvo/software-center/lobby-recommends-fixes: + - add test for recommender-opt-in behavior + + [ Kiwinote ] + * softwarecenter/db/update.py: + - index 'Keywords' entries from desktop files in addition to the + already supported 'X-AppInstall-Keywords' entries + * softwarecenter/ui/gtk3/widgets/exhibits.py: + - increase delay between the 'load-finished' signal from webkit and us + actually trying to get a pixbuf of the surface - this means that the + initial banner is actually rendered properly rather than plain white + * softwarecenter/ui/gtk3/widgets/recommendations.py: + refactor _show_opt_in_view: + - use normal sized text rather than big text + - use saner padding values + - resolve unescaped markup issues + - line wrap text so we don't get horizontal scrollbar (LP: #933497) + + [ Gary Lasker ] + * lp:~gary-lasker/software-center/get-installed-apps-instead-of-pkgs-tweaks: + - unit test and other small needed fixes, further simplification of + the recommender UUID code + * lp:~gary-lasker/software-center/lobby-recommends-fixes: + - fix recommender opt-in + + -- Michael Vogt Thu, 01 Mar 2012 20:11:27 +0100 + software-center (5.1.10) precise; urgency=low [ Michael Vogt ] diff -Nru software-center-5.1.10/setup.py software-center-5.1.11/setup.py --- software-center-5.1.10/setup.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/setup.py 2012-03-01 11:30:44.000000000 +0000 @@ -94,6 +94,8 @@ "utils/update-software-center", "utils/update-software-center-channels", "utils/update-software-center-agent", + # generic helpers + "utils/expunge-cache.py", ] + glob.glob("utils/piston-helpers/*.py"), packages=['softwarecenter', 'softwarecenter.backend', diff -Nru software-center-5.1.10/softwarecenter/backend/piston/sreclient_pristine.py software-center-5.1.11/softwarecenter/backend/piston/sreclient_pristine.py --- software-center-5.1.10/softwarecenter/backend/piston/sreclient_pristine.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/softwarecenter/backend/piston/sreclient_pristine.py 2012-03-01 11:30:44.000000000 +0000 @@ -40,6 +40,7 @@ scheme=PUBLIC_API_SCHEME) @oauth_protected + @returns_json def recommend_me(self): return self._get('recommend_me/', scheme=AUTHENTICATED_API_SCHEME) diff -Nru software-center-5.1.10/softwarecenter/backend/recagent.py software-center-5.1.11/softwarecenter/backend/recagent.py --- software-center-5.1.10/softwarecenter/backend/recagent.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/softwarecenter/backend/recagent.py 2012-03-01 11:30:44.000000000 +0000 @@ -25,6 +25,10 @@ import softwarecenter.paths from spawn_helper import SpawnHelper +from softwarecenter.config import get_config +from softwarecenter.db.utils import get_installed_apps_list +from softwarecenter.utils import get_uuid + LOG = logging.getLogger(__name__) class RecommenderAgent(GObject.GObject): @@ -38,11 +42,11 @@ GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT,), ), - "submit-profile" : (GObject.SIGNAL_RUN_LAST, + "submit-profile-finished" : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, - (GObject.TYPE_PYOBJECT,), + (GObject.TYPE_PYOBJECT, str), ), - "submit-anon-profile" : (GObject.SIGNAL_RUN_LAST, + "submit-anon-profile-finished" : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, (GObject.TYPE_PYOBJECT,), ), @@ -71,7 +75,8 @@ def __init__(self, xid=None): GObject.GObject.__init__(self) self.xid = xid - + self.recommender_uuid = self._get_recommender_uuid() + def query_server_status(self): # build the command spawner = SpawnHelper() @@ -82,7 +87,19 @@ spawner.run_generic_piston_helper( "SoftwareCenterRecommenderAPI", "server_status") - def query_submit_profile(self, data): + def post_submit_profile(self, db): + """ This will post the users profile to the recommender server + and also generate the UUID for the user if that is not + there yet + """ + # if we have not already set a recommender UUID, now is the time + # to do it + if not self.recommender_uuid: + self.recommender_uuid = get_uuid() + installed_pkglist = [app.pkgname + for app in get_installed_apps_list(db)] + data = self._generate_submit_profile_data(self.recommender_uuid, + installed_pkglist) # build the command spawner = SpawnHelper() spawner.parent_xid = self.xid @@ -94,7 +111,7 @@ "submit_profile", data=data) - def query_submit_anon_profile(self, uuid, installed_packages, extra): + def post_submit_anon_profile(self, uuid, installed_packages, extra): # build the command spawner = SpawnHelper() spawner.parent_xid = self.xid @@ -166,7 +183,9 @@ self.emit("profile", piston_profile) def _on_submit_profile_data(self, spawner, piston_submit_profile): - self.emit("submit-profile", piston_submit_profile) + self.emit("submit-profile-finished", + piston_submit_profile, + self.recommender_uuid) def _on_submit_anon_profile_data(self, spawner, piston_submit_anon_profile): self.emit("submit-anon_profile", piston_submit_anon_profile) @@ -182,6 +201,27 @@ def _on_recommend_top_data(self, spawner, piston_top_apps): self.emit("recommend-top", piston_top_apps) + + def _get_recommender_uuid(self): + """ returns the recommender UUID value, which can be empty if it + has not yet been set (indicating that the user has not yet + opted-in to the recommender service) + """ + config = get_config() + if config.has_option("general", "recommender_uuid"): + recommender_uuid = config.get("general", "recommender_uuid") + if recommender_uuid: + return recommender_uuid + return "" + + def _generate_submit_profile_data(self, recommender_uuid, package_list): + submit_profile_data = [ + { + 'uuid': recommender_uuid, + 'package_list': package_list + } + ] + return submit_profile_data if __name__ == "__main__": diff -Nru software-center-5.1.10/softwarecenter/db/categories.py software-center-5.1.11/softwarecenter/db/categories.py --- software-center-5.1.10/softwarecenter/db/categories.py 2012-02-23 15:41:09.000000000 +0000 +++ software-center-5.1.11/softwarecenter/db/categories.py 2012-03-01 11:30:44.000000000 +0000 @@ -153,7 +153,7 @@ def _recommend_me_result(self, recommender_agent, result_list): pkgs = [] - for item in result_list['recommendations']: + for item in result_list['data']: pkgs.append(item['package_name']) self.query = get_query_for_pkgnames(pkgs) self.emit("needs-refresh") diff -Nru software-center-5.1.10/softwarecenter/db/database.py software-center-5.1.11/softwarecenter/db/database.py --- software-center-5.1.10/softwarecenter/db/database.py 2012-02-23 15:20:28.000000000 +0000 +++ software-center-5.1.11/softwarecenter/db/database.py 2012-03-01 11:30:44.000000000 +0000 @@ -374,6 +374,13 @@ pass return summary + def get_application(self, doc): + """ Return a application from a xapian document """ + appname = self.get_appname(doc) + pkgname = self.get_pkgname(doc) + iconname = self.get_iconname(doc) + return Application(appname, pkgname, iconname) + def get_pkgname(self, doc): """ Return a packagename from a xapian document """ pkgname = doc.get_value(XapianValues.PKGNAME) diff -Nru software-center-5.1.10/softwarecenter/db/update.py software-center-5.1.11/softwarecenter/db/update.py --- software-center-5.1.10/softwarecenter/db/update.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/softwarecenter/db/update.py 2012-03-01 11:30:44.000000000 +0000 @@ -68,6 +68,7 @@ from softwarecenter.db.pkginfo import get_pkg_info from softwarecenter.distro import get_current_arch, get_foreign_architectures +from softwarecenter.region import get_region_cached, REGION_BLACKLIST_TAG # weights for the different fields WEIGHT_DESKTOP_NAME = 10 @@ -862,10 +863,18 @@ # (deb)tags (in addition to the pkgname debtags if parser.has_option_desktop("X-AppInstall-Tags"): + # register tags tags = parser.get_desktop("X-AppInstall-Tags") if tags: for tag in tags.split(","): doc.add_term("XT"+tag.strip()) + # ENFORCE region blacklist by not registering the app at all + region = get_region_cached() + if region: + countrycode = region["countrycode"].lower() + if "%s%s" % (REGION_BLACKLIST_TAG, countrycode) in tags: + LOG.info("skipping region restricted app: '%s'" % name) + return None # popcon # FIXME: popularity not only based on popcon but also @@ -956,8 +965,12 @@ doc.add_term("XOS"+origin.site) # add our keywords (with high priority) - if parser.has_option_desktop("X-AppInstall-Keywords"): + keywords = None + if parser.has_option_desktop("Keywords"): + keywords = parser.get_desktop("Keywords") + elif parser.has_option_desktop("X-AppInstall-Keywords"): keywords = parser.get_desktop("X-AppInstall-Keywords") + if keywords: for s in keywords.split(";"): if s: term_generator.index_text_without_positions(s, WEIGHT_DESKTOP_KEYWORD) diff -Nru software-center-5.1.10/softwarecenter/db/utils.py software-center-5.1.11/softwarecenter/db/utils.py --- software-center-5.1.10/softwarecenter/db/utils.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/softwarecenter/db/utils.py 2012-03-01 11:30:44.000000000 +0000 @@ -30,6 +30,17 @@ xapian.Query("AP"+pkgname)) return query +def get_installed_apps_list(db): + """ return a list of installed applications """ + apps = set() + for doc in db: + if db.get_appname(doc): + pkgname = db.get_pkgname(doc) + if (pkgname in db._aptcache and + db._aptcache[pkgname].is_installed): + apps.add(db.get_application(doc)) + return apps + def get_installed_package_list(): """ return a set of all of the currently installed packages """ from softwarecenter.db.pkginfo import get_pkg_info diff -Nru software-center-5.1.10/softwarecenter/region.py software-center-5.1.11/softwarecenter/region.py --- software-center-5.1.10/softwarecenter/region.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/softwarecenter/region.py 2012-03-01 11:30:44.000000000 +0000 @@ -36,6 +36,8 @@ # the prefix tag for regions that the software is most useful in REGIONTAG = "iso3166::" +# blacklist this region +REGION_BLACKLIST_TAG = "blacklist-iso3166::" def get_region_name(countrycode): """ return translated region name from countrycode using iso3166 """ diff -Nru software-center-5.1.10/softwarecenter/testutils.py software-center-5.1.11/softwarecenter/testutils.py --- software-center-5.1.10/softwarecenter/testutils.py 2012-02-23 15:41:09.000000000 +0000 +++ software-center-5.1.11/softwarecenter/testutils.py 2012-03-01 19:08:21.000000000 +0000 @@ -194,7 +194,7 @@ def make_recommender_agent_recommend_me_dict(): # best to have a list of likely not-installed items app_dict = { - u'recommendations': [ + u'data': [ { u'package_name': u'clementine' }, diff -Nru software-center-5.1.10/softwarecenter/ui/gtk3/app.py software-center-5.1.11/softwarecenter/ui/gtk3/app.py --- software-center-5.1.10/softwarecenter/ui/gtk3/app.py 2012-02-16 20:30:28.000000000 +0000 +++ software-center-5.1.11/softwarecenter/ui/gtk3/app.py 2012-03-01 11:30:44.000000000 +0000 @@ -390,6 +390,10 @@ # running the agent will trigger a db reload so we do it later GObject.timeout_add_seconds(30, self._run_software_center_agent) + + # keep the cache clean + GObject.timeout_add_seconds(15, self._run_expunge_cache_helper) + # TODO: Remove the following two lines once we have remove repository # support in aptdaemon (see LP: #723911) file_menu = self.builder.get_object("menu1") @@ -419,6 +423,16 @@ GObject.child_watch_add( pid, self._on_update_software_center_agent_finished) + def _run_expunge_cache_helper(self): + """ helper that expires the piston-mini-client cache """ + sc_expunge_cache = os.path.join( + self.datadir, "expunge-cache.py") + (pid, stdin, stdout, stderr) = GObject.spawn_async( + [sc_expunge_cache, + "--by-unsuccessful-http-states", + softwarecenter.paths.SOFTWARE_CENTER_CACHE_DIR, + ]) + def _rebuild_and_reopen_local_db(self, pathname): """ helper that rebuilds a db and reopens it """ from softwarecenter.db.update import rebuild_database @@ -466,7 +480,7 @@ #~ def on_installed_pane_created(self, widget): #~ pass - def _on_recommendations_opt_in(self, recommender_uuid): + def _on_recommendations_opt_in(self, agent, recommender_uuid): self.recommender_uuid = recommender_uuid def _on_recommendations_opt_out(self): @@ -1175,8 +1189,10 @@ # if the pkg is installed, show it in the installed pane if (app.pkgname in self.cache and self.cache[app.pkgname].installed): - self.installed_pane.init_view() - self.installed_pane.show_app(app) + with ExecutionTime("installed_pane.init_view()"): + self.installed_pane.init_view() + with ExecutionTime("installed_pane.show_app()"): + self.installed_pane.show_app(app) else: self.available_pane.init_view() self.available_pane.show_app(app) diff -Nru software-center-5.1.10/softwarecenter/ui/gtk3/panes/availablepane.py software-center-5.1.11/softwarecenter/ui/gtk3/panes/availablepane.py --- software-center-5.1.10/softwarecenter/ui/gtk3/panes/availablepane.py 2012-02-16 20:18:48.000000000 +0000 +++ software-center-5.1.11/softwarecenter/ui/gtk3/panes/availablepane.py 2012-03-01 11:30:44.000000000 +0000 @@ -118,6 +118,7 @@ return self.show_appview_spinner() + window = self.get_window() if window is not None: window.set_cursor(self.busy_cursor) diff -Nru software-center-5.1.10/softwarecenter/ui/gtk3/panes/historypane.py software-center-5.1.11/softwarecenter/ui/gtk3/panes/historypane.py --- software-center-5.1.10/softwarecenter/ui/gtk3/panes/historypane.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/softwarecenter/ui/gtk3/panes/historypane.py 2012-03-01 11:31:37.000000000 +0000 @@ -19,12 +19,13 @@ from gi.repository import GObject from gi.repository import Gtk, Gdk + import logging import datetime from gettext import gettext as _ -from softwarecenter.ui.gtk3.widgets.spinner import SpinnerView +from softwarecenter.ui.gtk3.widgets.spinner import SpinnerNotebook from basepane import BasePane from softwarecenter.enums import Icons from softwarecenter.ui.gtk3.session.viewmanager import get_viewmanager @@ -119,12 +120,8 @@ self.history_view.add(self.view) # make a spinner to display while history is loading - self.spinner_view = SpinnerView(_('Loading history')) - self.spinner_notebook = Gtk.Notebook() - self.spinner_notebook.set_show_tabs(False) - self.spinner_notebook.set_show_border(False) - self.spinner_notebook.append_page(self.history_view, None) - self.spinner_notebook.append_page(self.spinner_view, None) + self.spinner_notebook = SpinnerNotebook( + self.history_view, _('Loading history')) self.pack_start(self.spinner_notebook, True, True, 0) @@ -159,11 +156,9 @@ self.realize() window = self.get_window() window.set_cursor(self.busy_cursor) - self.spinner_view.start() - self.spinner_notebook.set_current_page(self.PAGE_SPINNER) + self.spinner_notebook.show_spinner() self.load_and_parse_history() - self.spinner_notebook.set_current_page(self.PAGE_HISTORY_VIEW) - self.spinner_view.stop() + self.spinner_notebook.hide_spinner() self._set_actions_sensitive(True) window.set_cursor(None) self.emit("history-pane-created") diff -Nru software-center-5.1.10/softwarecenter/ui/gtk3/panes/installedpane.py software-center-5.1.11/softwarecenter/ui/gtk3/panes/installedpane.py --- software-center-5.1.10/softwarecenter/ui/gtk3/panes/installedpane.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/softwarecenter/ui/gtk3/panes/installedpane.py 2012-03-01 11:30:44.000000000 +0000 @@ -37,7 +37,7 @@ AppTreeStore, CategoryRowReference) from softwarecenter.ui.gtk3.widgets.menubutton import MenuButton from softwarecenter.ui.gtk3.widgets.oneconfviews import OneConfViews -from softwarecenter.ui.gtk3.widgets.spinner import SpinnerView +from softwarecenter.ui.gtk3.widgets.spinner import SpinnerNotebook from softwarecenter.ui.gtk3.views.appview import AppView from softwarepane import SoftwarePane from softwarecenter.backend.oneconfhandler import get_oneconf_handler @@ -115,7 +115,8 @@ self.visible_cats = {} def init_view(self): - if self.view_initialized: return + if self.view_initialized: + return SoftwarePane.init_view(self) @@ -187,15 +188,12 @@ # we do not support the search aid feature in the installedview self.box_app_list.remove(self.search_aid) - + + # remove here + self.box_app_list.remove(self.app_view) + # create a local spinner notebook for the installed view - self.installed_spinner_view = SpinnerView() - self.installed_spinner_notebook = Gtk.Notebook() - self.installed_spinner_notebook.set_show_tabs(False) - self.installed_spinner_notebook.set_show_border(False) - self.installed_spinner_notebook.append_page(self.installed_spinner_view, None) - self.box_app_list.remove(self.app_view) - self.installed_spinner_notebook.append_page(self.app_view, None) + self.installed_spinner_notebook = SpinnerNotebook(self.app_view) self.computerpane.pack2(self.installed_spinner_notebook, True, True) self.show_installed_view_spinner() @@ -217,20 +215,11 @@ def show_installed_view_spinner(self): """ display the local spinner for the installed view panel """ - self.installed_spinner_view.stop() - self.installed_spinner_notebook.set_current_page(self.PAGE_SPINNER) - # "mask" the spinner view momentarily to prevent it from flashing into - # view in the case of short delays where it isn't actually needed - GObject.timeout_add(100, self._unmask_installed_view_spinner) - - def _unmask_installed_view_spinner(self): - self.installed_spinner_view.start() - return False + self.installed_spinner_notebook.show_spinner() def hide_installed_view_spinner(self): """ hide the local spinner for the installed view panel """ - self.installed_spinner_notebook.set_current_page(self.PAGE_INSTALLED) - self.installed_spinner_view.stop() + self.installed_spinner_notebook.hide_spinner() def _selected_computer_changed(self, oneconf_pickler, hostid, hostname): if self.current_hostid == hostid: diff -Nru software-center-5.1.10/softwarecenter/ui/gtk3/panes/softwarepane.py software-center-5.1.11/softwarecenter/ui/gtk3/panes/softwarepane.py --- software-center-5.1.10/softwarecenter/ui/gtk3/panes/softwarepane.py 2012-02-16 20:18:48.000000000 +0000 +++ software-center-5.1.11/softwarecenter/ui/gtk3/panes/softwarepane.py 2012-03-01 11:30:44.000000000 +0000 @@ -39,7 +39,7 @@ from softwarecenter.ui.gtk3.session.viewmanager import get_viewmanager from softwarecenter.ui.gtk3.widgets.actionbar import ActionBar -from softwarecenter.ui.gtk3.widgets.spinner import SpinnerView +from softwarecenter.ui.gtk3.widgets.spinner import SpinnerNotebook from softwarecenter.ui.gtk3.widgets.searchaid import SearchAid from softwarecenter.ui.gtk3.views.appview import AppView @@ -179,18 +179,8 @@ if not "SOFTWARE_CENTER_DEBUG_TABS" in os.environ: self.notebook.set_show_tabs(False) self.notebook.set_show_border(False) - # an empty notebook, where the details view will eventually go - self.details_notebook = Gtk.Notebook() - self.details_notebook.set_show_border(False) # make a spinner view to display while the applist is loading - self.spinner_view = SpinnerView() - self.spinner_notebook = Gtk.Notebook() - self.spinner_notebook.set_show_tabs(False) - self.spinner_notebook.set_show_border(False) - self.spinner_notebook.append_page(self.notebook, None) - self.spinner_notebook.append_page(self.details_notebook, None) - self.spinner_notebook.append_page(self.spinner_view, None) - + self.spinner_notebook = SpinnerNotebook(self.notebook) self.pack_start(self.spinner_notebook, True, True, 0) # add a bar at the bottom (hidden by default) for contextual actions @@ -317,25 +307,15 @@ def show_appview_spinner(self): """ display the spinner in the appview panel """ LOG.debug("show_appview_spinner") + # FIXME: totally the wrong place! if not self.state.search_term: self.action_bar.clear() - self.spinner_view.stop() - self.spinner_notebook.set_current_page(SoftwarePane.Pages.SPINNER) - # "mask" the spinner view momentarily to prevent it from flashing into - # view in the case of short delays where it isn't actually needed - GObject.timeout_add(100, self._unmask_appview_spinner) - - def _unmask_appview_spinner(self): - LOG.debug("_unmask_appview_spinner") - self.spinner_view.start() - return False + self.spinner_notebook.show_spinner() def hide_appview_spinner(self): """ hide the spinner and display the appview in the panel """ LOG.debug("hide_appview_spinner") - self.spinner_notebook.set_current_page( - SoftwarePane.Pages.APPVIEW) - self.spinner_view.stop() + self.spinner_notebook.hide_spinner() def set_section(self, section): self.section = section diff -Nru software-center-5.1.10/softwarecenter/ui/gtk3/views/appdetailsview.py software-center-5.1.11/softwarecenter/ui/gtk3/views/appdetailsview.py --- software-center-5.1.10/softwarecenter/ui/gtk3/views/appdetailsview.py 2012-02-16 20:04:43.000000000 +0000 +++ software-center-5.1.11/softwarecenter/ui/gtk3/views/appdetailsview.py 2012-03-01 11:30:44.000000000 +0000 @@ -1515,8 +1515,12 @@ def _update_all(self, app_details, skip_update_addons=False): # reset view to top left - self.get_vadjustment().set_value(0) - self.get_hadjustment().set_value(0) + vadj = self.get_vadjustment() + hadj = self.get_hadjustment() + if vadj: + vadj.set_value(0) + if hadj: + hadj.set_value(0) # set button sensitive again self.pkg_statusbar.button.set_sensitive(True) diff -Nru software-center-5.1.10/softwarecenter/ui/gtk3/widgets/containers.py software-center-5.1.11/softwarecenter/ui/gtk3/widgets/containers.py --- software-center-5.1.10/softwarecenter/ui/gtk3/widgets/containers.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/softwarecenter/ui/gtk3/widgets/containers.py 2012-03-01 11:30:44.000000000 +0000 @@ -9,7 +9,7 @@ from buttons import MoreLink from softwarecenter.ui.gtk3.em import StockEms from softwarecenter.ui.gtk3.drawing import rounded_rect -from softwarecenter.ui.gtk3.widgets.spinner import SpinnerView +from softwarecenter.ui.gtk3.widgets.spinner import SpinnerNotebook class FlowableGrid(Gtk.Fixed): @@ -513,16 +513,9 @@ self.box.pack_start(self.header_alignment, False, False, 0) # make the content box self.content_box = Gtk.Box.new(orientation, spacing) - # and a spinner with optional label (to be used only if needed) - # TODO: cosmetic tweak needed - draw a line at the top of the spinner - # area so that the header has a proper border - self.spinner = SpinnerView() + self.content_box.show() # finally, a notebook for the spinner and the content box to share - self.spinner_notebook = Gtk.Notebook() - self.spinner_notebook.set_show_tabs(False) - self.spinner_notebook.set_show_border(False) - self.spinner_notebook.append_page(self.content_box, None) - self.spinner_notebook.append_page(self.spinner, None) + self.spinner_notebook = SpinnerNotebook(self.content_box) self.box.add(self.spinner_notebook) return @@ -544,16 +537,6 @@ def pack_end(self, *args, **kwargs): return self.content_box.pack_end(*args, **kwargs) - def show_spinner(self): - self.spinner.start() - self.spinner.show() - self.spinner_notebook.set_current_page(self.SPINNER) - - def show_content(self): - self.spinner.stop() - self.spinner.hide() - self.spinner_notebook.set_current_page(self.CONTENT) - # XXX: non-functional with current code... #~ def set_header_expand(self, expand): #~ alignment = self.header_alignment diff -Nru software-center-5.1.10/softwarecenter/ui/gtk3/widgets/exhibits.py software-center-5.1.11/softwarecenter/ui/gtk3/widgets/exhibits.py --- software-center-5.1.10/softwarecenter/ui/gtk3/widgets/exhibits.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/softwarecenter/ui/gtk3/widgets/exhibits.py 2012-03-01 11:30:44.000000000 +0000 @@ -126,7 +126,7 @@ if view.get_property("load-status") == WebKit.LoadStatus.FINISHED: # this needs to run with a timeout because otherwise the # status is emited before the offscreen image is finihsed - GObject.timeout_add(1, lambda: self.emit("render-finished")) + GObject.timeout_add(100, lambda: self.emit("render-finished")) def on_download_error(self, loader, exception, error): LOG.warn("download failed: '%s', '%s'" % (exception, error)) diff -Nru software-center-5.1.10/softwarecenter/ui/gtk3/widgets/recommendations.py software-center-5.1.11/softwarecenter/ui/gtk3/widgets/recommendations.py --- software-center-5.1.10/softwarecenter/ui/gtk3/widgets/recommendations.py 2012-02-23 15:41:09.000000000 +0000 +++ software-center-5.1.11/softwarecenter/ui/gtk3/widgets/recommendations.py 2012-03-01 19:08:21.000000000 +0000 @@ -28,9 +28,7 @@ from softwarecenter.db.categories import (RecommendedForYouCategory, AppRecommendationsCategory) from softwarecenter.backend.recagent import RecommenderAgent -from softwarecenter.db.utils import get_installed_package_list -from softwarecenter.utils import get_uuid -from softwarecenter.config import get_config + LOG = logging.getLogger(__name__) @@ -80,42 +78,39 @@ RecommendationsPanel.__init__(self, catview) self.set_header_label(_(u"Recommended for You")) - self.recommender_uuid = "" - # FIXME: probs should just pass this on in instead of reading config - config = get_config() - if config.has_option("general", "recommender_uuid"): - self.recommender_uuid = config.get("general", - "recommender_uuid") - - if not self.recommender_uuid: - self._show_opt_in_view() - else: + # if we already have a recommender UUID, then the user is already + # opted-in to the recommender service + self.recommended_for_you_content = None + if self.recommender_agent.recommender_uuid: self._update_recommended_for_you_content() + else: + self._show_opt_in_view() + def _show_opt_in_view(self): + # opt in box + vbox = Gtk.Box.new(Gtk.Orientation.VERTICAL, StockEms.MEDIUM) + vbox.set_border_width(StockEms.LARGE) + self.opt_in_vbox = vbox # for tests + self.recommended_for_you_content = vbox # hook it up to the rest + self.add(self.recommended_for_you_content) - self.header_implements_more_button() - def _show_opt_in_view(self): - self.opt_in_vbox = Gtk.VBox(spacing=12) - self.opt_in_button = Gtk.Button() - opt_in_button_label = Gtk.Label() - opt_in_button_label.set_markup('%s' % _("Turn On Recommendations")) - opt_in_button_label.set_padding(StockEms.SMALL, StockEms.SMALL) - self.opt_in_button.add(opt_in_button_label) - self.opt_in_button.connect("clicked", self._on_opt_in_button_clicked) - opt_in_button_hbox = Gtk.HBox() - opt_in_button_hbox.pack_start(self.opt_in_button, False, False, 0) - opt_in_text = _("To make recommendations, Ubuntu Software Center " - "will occasionally send to Canonical an anonymous list " - "of software currently installed.") - opt_in_label = Gtk.Label() - opt_in_label.set_markup('%s' % opt_in_text) - opt_in_label.set_use_markup(True) - self.opt_in_vbox.pack_start(opt_in_button_hbox, False, False, 0) - self.opt_in_vbox.pack_start(opt_in_label, False, False, 10) - self.recommended_for_you_content = Gtk.Alignment.new(0.5, 0.5, 1.0, 1.0) - self.recommended_for_you_content.set_padding(50, 50, 50, 50) - self.recommended_for_you_content.add(self.opt_in_vbox) + # opt in button + button = Gtk.Button(_("Turn On Recommendations")) + button.connect("clicked", self._on_opt_in_button_clicked) + hbox = Gtk.Box(Gtk.Orientation.HORIZONTAL) + hbox.pack_start(button, False, False, 0) + vbox.pack_start(hbox, False, False, 0) + self.opt_in_button = button # for tests + + # opt in text + text = _("To make recommendations, Ubuntu Software Center " + "will occasionally send to Canonical an anonymous list " + "of software currently installed.") + label = Gtk.Label(text) + label.set_alignment(0, 0.5) + label.set_line_wrap(True) + vbox.pack_start(label, False, False, 0) def _on_opt_in_button_clicked(self, button): # we upload the user profile here, and only after this is finished @@ -130,24 +125,19 @@ self._upload_user_profile() def _upload_user_profile(self): - self.spinner.set_text(_("Submitting inventory…")) - self.show_spinner() - self.recommender_uuid = get_uuid() - installed_pkglist = list(get_installed_package_list()) - self.recommender_agent.connect("submit-profile", + self.spinner_notebook.show_spinner(_("Submitting inventory…")) + self.recommender_agent.connect("submit-profile-finished", self._on_profile_submitted) self.recommender_agent.connect("error", self._on_profile_submitted_error) - self.recommender_agent.query_submit_profile( - self._generate_submit_profile_data(self.recommender_uuid, - installed_pkglist)) + self.recommender_agent.post_submit_profile(self.catview.db) - def _on_profile_submitted(self): + def _on_profile_submitted(self, agent, profile, recommender_uuid): # after the user profile data has been uploaded, make the request # and load the the recommended_for_you content LOG.debug("The recommendations profile has been successfully " "submitted to the recommender agent") - self.emit("recommendations-opt-in", self.recommender_uuid) + self.emit("recommendations-opt-in", recommender_uuid) self._update_recommended_for_you_content() def _on_profile_submitted_error(self, agent, msg): @@ -157,9 +147,15 @@ self._hide_recommended_for_you_panel() def _update_recommended_for_you_content(self): + # destroy the old content to ensure we don't see it twice + # (also removes the opt-in panel if it was there) + if self.recommended_for_you_content: + self.recommended_for_you_content.destroy() + # add the new stuff + self.header_implements_more_button() self.recommended_for_you_content = FlowableGrid() - self.spinner.set_text(_("Receiving recommendations…")) - self.show_spinner() + self.add(self.recommended_for_you_content) + self.spinner_notebook.show_spinner(_("Receiving recommendations…")) # get the recommendations from the recommender agent self.recommended_for_you_cat = RecommendedForYouCategory() self.recommended_for_you_cat.connect( @@ -175,7 +171,7 @@ self.catview._add_tiles_to_flowgrid(docs, self.recommended_for_you_content, 8) self.recommended_for_you_content.show_all() - self.show_content() + self.spinner_notebook.hide_spinner() self.more.connect('clicked', self.catview.on_category_clicked, cat) @@ -195,16 +191,6 @@ # and hide the pane self.hide() - def _generate_submit_profile_data(self, - recommender_uuid, - package_list): - submit_profile_data = [ - { - 'uuid': recommender_uuid, - 'package_list': package_list - } - ] - return submit_profile_data class RecommendationsPanelDetails(RecommendationsPanel): """ @@ -223,8 +209,7 @@ def _update_app_recommendations_content(self): self.app_recommendations_content.remove_all() - self.spinner.set_text(_("Receiving recommendations…")) - self.show_spinner() + self.spinner_notebook.show_spinner(_("Receiving recommendations…")) # get the recommendations from the recommender agent self.app_recommendations_cat = AppRecommendationsCategory(self.pkgname) self.app_recommendations_cat.connect( @@ -239,8 +224,8 @@ if len(docs) > 0: self.catview._add_tiles_to_flowgrid(docs, self.app_recommendations_content, 8) - self.app_recommendations_content.show_all() - self.show_content() + self.show_all() + self.spinner_notebook.hide_spinner() else: self._hide_app_recommendations_panel() return @@ -256,14 +241,29 @@ self.hide() - -def get_test_window_recommendations_panel_lobby(): +# test helpers +def get_test_window(): import softwarecenter.log softwarecenter.log.root.setLevel(level=logging.DEBUG) fmt = logging.Formatter("%(name)s - %(message)s", None) softwarecenter.log.handler.setFormatter(fmt) - view = RecommendationsPanelLobby() + + # this is *way* to complicated we should *not* need a CatView + # here! see FIXME in RecommendationsPanel.__init__() + from softwarecenter.ui.gtk3.views.catview_gtk import CategoriesViewGtk + from softwarecenter.testutils import ( + get_test_db, get_test_pkg_info, get_test_gtk3_icon_cache) + cache = get_test_pkg_info() + db = get_test_db() + icons = get_test_gtk3_icon_cache() + catview = CategoriesViewGtk(softwarecenter.paths.datadir, + softwarecenter.paths.APP_INSTALL_PATH, + cache, + db, + icons) + + view = RecommendationsPanelLobby(catview) win = Gtk.Window() win.connect("destroy", lambda x: Gtk.main_quit()) @@ -276,5 +276,5 @@ if __name__ == "__main__": - win = get_test_window_recommendations_panel_lobby() + win = get_test_window() Gtk.main() diff -Nru software-center-5.1.10/softwarecenter/ui/gtk3/widgets/spinner.py software-center-5.1.11/softwarecenter/ui/gtk3/widgets/spinner.py --- software-center-5.1.10/softwarecenter/ui/gtk3/widgets/spinner.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/softwarecenter/ui/gtk3/widgets/spinner.py 2012-03-01 11:31:29.000000000 +0000 @@ -16,40 +16,12 @@ # this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - import gi gi.require_version("Gtk", "3.0") -from gi.repository import Gtk, GObject - -from softwarecenter.paths import IMAGE_LOADING_INSTALLED +import os -class Spinner(object): - """ - A factory to create the appropriate spinner based on whether - Gtk.Spinner is available (see LP: #637422, LP: #624204) - """ - def __new__(cls, *args, **kwargs): - try: - spinner = Gtk.Spinner() - except AttributeError: - spinner = GifSpinner() - return spinner +from gi.repository import Gtk, GObject -class GifSpinner(Gtk.VBox): - """ - an alternative spinner implementation that uses an animated gif - """ - def __init__(self): - GObject.GObject.__init__(self) - self.image = Gtk.Image() - self.image.set_from_file(IMAGE_LOADING_INSTALLED) - self.add(self.image) - - def start(self): - pass - def stop(self): - pass - class SpinnerView(Gtk.Viewport): """ a panel that contains a spinner preset to a standard size and centered @@ -57,7 +29,7 @@ """ def __init__(self, label_text=""): Gtk.Viewport.__init__(self) - self.spinner = Spinner() + self.spinner = Gtk.Spinner() self.spinner.set_size_request(48, 48) # use a table for the spinner (otherwise the spinner is massive!) @@ -73,14 +45,14 @@ self.add(spinner_table) self.set_shadow_type(Gtk.ShadowType.NONE) - def start(self): + def start_and_show(self): """ start the spinner and show it """ self.spinner.start() self.spinner.show() - def stop(self): + def stop_and_hide(self): """ stop the spinner and hide it """ @@ -93,18 +65,59 @@ """ self.spinner_label.set_markup('%s' % spinner_text) +class SpinnerNotebook(Gtk.Notebook): + """ this provides a Gtk.Notebook that contains a content page + and a spinner page. + """ + -def get_test_spinner_window(): - spinner_view = SpinnerView() - spinner_view.start() + (CONTENT_PAGE, + SPINNER_PAGE) = range(2) + + def __init__(self, content, msg=""): + Gtk.Notebook.__init__(self) + self.spinner_view = SpinnerView(msg) + # its critical to show() the spinner early as otherwise + # gtk_notebook_set_active_page() will not switch to it + self.spinner_view.show() + if not "SOFTWARE_CENTER_DEBUG_TABS" in os.environ: + self.set_show_tabs(False) + self.set_show_border(False) + self.append_page(content, Gtk.Label("content")) + self.append_page(self.spinner_view, Gtk.Label("spinner")) + + def _unmask_view_spinner(self): + # start is actually start_and_show() + self.spinner_view.start_and_show() + return False + + def show_spinner(self, msg=""): + """ show the spinner page with a alternative message """ + if msg: + self.spinner_view.set_text(msg) + # "mask" the spinner view momentarily to prevent it from flashing into + # view in the case of short delays where it isn't actually needed + self.spinner_view.stop_and_hide() + GObject.timeout_add(100, self._unmask_view_spinner) + self.set_current_page(self.SPINNER_PAGE) + + def hide_spinner(self): + """ hide the spinner page again and show the content page """ + self.spinner_view.stop_and_hide() + self.set_current_page(self.CONTENT_PAGE) + +def get_test_spinner_window(): + label = Gtk.Label("foo") + spinner_notebook = SpinnerNotebook(label, "random msg") window = Gtk.Window() - window.add(spinner_view) + window.add(spinner_notebook) window.set_size_request(600, 500) window.set_position(Gtk.WindowPosition.CENTER) window.show_all() window.connect('destroy', Gtk.main_quit) - spinner_view.set_text("Loading...") + spinner_notebook.show_spinner("Loading for 1s ...") + GObject.timeout_add_seconds(1, lambda: spinner_notebook.hide_spinner()) return window if __name__ == "__main__": diff -Nru software-center-5.1.10/softwarecenter/version.py software-center-5.1.11/softwarecenter/version.py --- software-center-5.1.10/softwarecenter/version.py 2012-02-23 16:37:23.000000000 +0000 +++ software-center-5.1.11/softwarecenter/version.py 2012-03-01 19:28:49.000000000 +0000 @@ -1,5 +1,5 @@ -VERSION='5.1.10' +VERSION='5.1.11' CODENAME='precise' DISTRO='Ubuntu' RELEASE='12.04' diff -Nru software-center-5.1.10/test/coverage_summary software-center-5.1.11/test/coverage_summary --- software-center-5.1.10/test/coverage_summary 2012-02-23 16:37:14.000000000 +0000 +++ software-center-5.1.11/test/coverage_summary 2012-03-01 19:28:38.000000000 +0000 @@ -1,131 +1,131 @@ Name Stmts Miss Cover -------------------------------------------------------------------------------------------------------------------------------------------- -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/__init__ 2 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/channel 182 24 87% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/channel_impl/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/channel_impl/aptchannels 144 49 66% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/fake_review_settings 77 33 57% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/installbackend 36 14 61% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/installbackend_impl/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/installbackend_impl/aptd 478 296 38% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/launchpad 184 112 39% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/login 9 2 78% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/login_sso 96 31 68% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/oneconfhandler/__init__ 17 5 71% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/oneconfhandler/core 98 15 85% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/piston/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/piston/rnrclient 44 29 34% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/piston/rnrclient_fake 147 80 46% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/piston/rnrclient_pristine 80 26 68% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/recagent 97 39 60% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/reviews/__init__ 358 135 62% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/reviews/rnr 206 102 50% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/scagent 78 26 67% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/spawn_helper 87 14 84% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/transactionswatcher 50 19 62% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/ubuntusso 75 38 49% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/unitylauncher 26 11 58% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/weblive 168 100 40% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/backend/weblive_pristine 145 84 42% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/cmdfinder 31 3 90% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/config 28 9 68% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/__init__ 8 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/appfilter 67 11 84% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/application 516 123 76% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/categories 301 32 89% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/database 342 89 74% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/debfile 124 35 72% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/enquire 147 6 96% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/history 36 13 64% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/history_impl/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/history_impl/apthistory 132 33 75% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/pkginfo 108 35 68% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/pkginfo_impl/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/pkginfo_impl/aptcache 520 148 72% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/update 642 67 90% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/db/utils 15 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/distro/Debian 88 66 25% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/distro/Ubuntu 126 51 60% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/distro/__init__ 90 43 52% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/enums 120 8 93% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/gwibber_helper 64 22 66% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/hw 11 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/i18n 46 4 91% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/log 62 14 77% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/netstatus 87 19 78% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/paths 53 13 75% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/plugin 62 4 94% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/region 78 5 94% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/testutils 108 4 96% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/toolkit 13 3 77% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/SimpleGtkbuilderApp 18 5 72% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/app 686 340 50% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/aptd_gtk3 44 36 18% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/dialogs/__init__ 67 24 64% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/dialogs/deauthorize_dialog 78 66 15% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/dialogs/dependency_dialogs 79 19 76% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/drawing 68 34 50% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/em 33 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/gmenusearch 82 37 55% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/models/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/models/appstore2 299 46 85% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/models/pendingstore 111 65 41% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/panes/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/panes/availablepane 419 139 67% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/panes/basepane 15 5 67% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/panes/globalpane 60 4 93% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/panes/historypane 261 21 92% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/panes/installedpane 420 114 73% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/panes/pendingpane 95 28 71% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/panes/softwarepane 312 74 76% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/panes/viewswitcher 174 70 60% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/review_gui_helper 807 320 60% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/session/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/session/appmanager 89 12 87% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/session/navhistory 167 17 90% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/session/viewmanager 129 31 76% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/shapes 157 97 38% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/utils 53 7 87% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/views/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/views/appdetailsview 1302 203 84% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/views/appview 125 6 95% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/views/catview_gtk 429 61 86% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/views/pkgnamesview 64 8 88% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/views/purchaseview 235 104 56% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/actionbar 230 81 65% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/apptreeview 465 260 44% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/backforward 108 23 79% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/buttons 437 84 81% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/cellrenderers 327 111 66% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/containers 408 29 93% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/description 837 448 46% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/exhibits 381 67 82% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/imagedialog 35 3 91% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/labels 59 11 81% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/menubutton 64 45 30% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/oneconfviews 98 45 54% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/recommendations 141 23 84% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/reviews 599 115 81% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/searchaid 194 64 67% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/searchentry 90 25 72% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/separators 27 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/spinner 57 10 82% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/stars 355 47 87% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/symbolic_icons 154 11 93% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/thumbnail 370 65 82% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/videoplayer 111 57 49% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/viewport 22 13 41% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/gtk3/widgets/weblivedialog 68 58 15% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/qml/__init__ 0 0 100% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/qml/categoriesmodel 53 21 60% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/qml/pkglist 132 64 52% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/ui/qml/reviewslist 46 3 93% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/utils 426 120 72% -/home/egon/devel/software-center/build-area/software-center-5.1.10/softwarecenter/version 4 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/__init__ 2 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/channel 182 19 90% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/channel_impl/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/channel_impl/aptchannels 144 46 68% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/fake_review_settings 77 33 57% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/installbackend 36 14 61% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/installbackend_impl/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/installbackend_impl/aptd 478 296 38% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/launchpad 184 112 39% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/login 9 2 78% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/login_sso 96 31 68% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/oneconfhandler/__init__ 17 5 71% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/oneconfhandler/core 98 15 85% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/piston/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/piston/rnrclient 44 29 34% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/piston/rnrclient_fake 147 80 46% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/piston/rnrclient_pristine 80 26 68% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/recagent 115 34 70% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/reviews/__init__ 358 135 62% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/reviews/rnr 206 102 50% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/scagent 78 26 67% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/spawn_helper 87 14 84% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/transactionswatcher 50 19 62% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/ubuntusso 75 38 49% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/unitylauncher 26 11 58% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/weblive 168 100 40% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/backend/weblive_pristine 145 84 42% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/cmdfinder 31 3 90% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/config 28 9 68% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/__init__ 8 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/appfilter 67 11 84% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/application 516 123 76% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/categories 301 36 88% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/database 347 89 74% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/debfile 124 35 72% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/enquire 147 6 96% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/history 36 13 64% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/history_impl/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/history_impl/apthistory 132 33 75% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/pkginfo 108 35 68% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/pkginfo_impl/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/pkginfo_impl/aptcache 520 148 72% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/update 653 63 90% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/db/utils 23 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/distro/Debian 88 66 25% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/distro/Ubuntu 126 51 60% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/distro/__init__ 90 43 52% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/enums 120 8 93% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/gwibber_helper 64 22 66% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/hw 11 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/i18n 46 4 91% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/log 62 14 77% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/netstatus 87 19 78% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/paths 53 13 75% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/plugin 62 4 94% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/region 79 5 94% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/testutils 108 4 96% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/toolkit 13 3 77% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/SimpleGtkbuilderApp 18 5 72% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/app 692 344 50% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/aptd_gtk3 44 36 18% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/dialogs/__init__ 67 24 64% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/dialogs/deauthorize_dialog 78 66 15% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/dialogs/dependency_dialogs 79 19 76% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/drawing 68 34 50% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/em 33 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/gmenusearch 82 37 55% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/models/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/models/appstore2 299 44 85% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/models/pendingstore 111 65 41% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/panes/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/panes/availablepane 419 140 67% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/panes/basepane 15 5 67% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/panes/globalpane 60 4 93% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/panes/historypane 254 22 91% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/panes/installedpane 410 115 72% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/panes/pendingpane 95 28 71% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/panes/softwarepane 297 74 75% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/panes/viewswitcher 174 70 60% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/review_gui_helper 807 320 60% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/session/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/session/appmanager 89 12 87% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/session/navhistory 167 17 90% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/session/viewmanager 129 31 76% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/shapes 157 97 38% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/utils 53 7 87% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/views/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/views/appdetailsview 1306 203 84% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/views/appview 125 6 95% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/views/catview_gtk 429 61 86% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/views/pkgnamesview 64 8 88% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/views/purchaseview 235 104 56% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/actionbar 230 81 65% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/apptreeview 465 260 44% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/backforward 108 23 79% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/buttons 437 81 81% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/cellrenderers 327 111 66% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/containers 396 28 93% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/description 837 448 46% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/exhibits 381 67 82% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/imagedialog 35 3 91% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/labels 59 11 81% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/menubutton 64 45 30% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/oneconfviews 98 45 54% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/recommendations 133 14 89% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/reviews 599 115 81% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/searchaid 194 64 67% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/searchentry 90 25 72% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/separators 27 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/spinner 64 2 97% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/stars 355 47 87% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/symbolic_icons 154 11 93% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/thumbnail 370 65 82% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/videoplayer 111 57 49% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/viewport 22 13 41% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/gtk3/widgets/weblivedialog 68 58 15% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/qml/__init__ 0 0 100% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/qml/categoriesmodel 53 21 60% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/qml/pkglist 132 64 52% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/ui/qml/reviewslist 46 3 93% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/utils 426 120 72% +/home/egon/devel/software-center/build-area/software-center-5.1.11/softwarecenter/version 4 0 100% __init__ 0 0 100% data/plugins/mock_plugin 4 0 100% gtk3/test_app_view 38 0 100% @@ -133,7 +133,7 @@ gtk3/test_appmanager 51 0 100% gtk3/test_appstore2 35 0 100% gtk3/test_appview 45 0 100% -gtk3/test_catview 114 0 100% +gtk3/test_catview 130 0 100% gtk3/test_custom_lists 38 0 100% gtk3/test_debfile_view 27 0 100% gtk3/test_dialogs 27 0 100% @@ -143,6 +143,7 @@ gtk3/test_navhistory 135 0 100% gtk3/test_panes 42 0 100% gtk3/test_purchase 61 0 100% +gtk3/test_recommendations_widgets 14 0 100% gtk3/test_reviews 129 2 98% gtk3/test_search 48 0 100% gtk3/test_unity_launcher_integration 66 0 100% @@ -155,7 +156,7 @@ test_categories 58 0 100% test_channels 25 0 100% test_cmdfiner 20 0 100% -test_database 402 34 92% +test_database 419 34 92% test_debfileapplication 67 1 99% test_description_norm 35 0 100% test_distro 20 0 100% @@ -175,7 +176,7 @@ test_ppa_iconfilename 41 1 98% test_purchase_backend 47 23 51% test_pyflakes 9 0 100% -test_recagent 84 19 77% +test_recagent 101 20 80% test_region 46 0 100% test_reinstall_purchased 140 0 100% test_rnr_api 14 0 100% @@ -183,9 +184,9 @@ test_startup 39 24 38% test_testutils 37 0 100% test_ubuntu_sso_api 19 0 100% -test_utils 75 11 85% +test_utils 93 11 88% test_where_is_it 51 9 82% test_xapian 71 1 99% test_xapian_query 53 1 98% -------------------------------------------------------------------------------------------------------------------------------------------- -TOTAL 23592 6214 74% +TOTAL 23682 6186 74% diff -Nru software-center-5.1.10/test/gtk3/test_catview.py software-center-5.1.11/test/gtk3/test_catview.py --- software-center-5.1.10/test/gtk3/test_catview.py 2012-02-23 15:41:09.000000000 +0000 +++ software-center-5.1.11/test/gtk3/test_catview.py 2012-03-01 19:08:21.000000000 +0000 @@ -1,4 +1,4 @@ -from gi.repository import Gtk +from gi.repository import Gtk, GObject import time import unittest from mock import patch @@ -87,6 +87,13 @@ win.destroy() def test_subcatview_recommended_for_you_opt_in_display(self): + + # patch the recommender UUID value to insure that we are not opted-in for this test + get_recommender_uuid_patcher = patch('softwarecenter.backend.recagent.RecommenderAgent._get_recommender_uuid') + self.addCleanup(get_recommender_uuid_patcher.stop) + mock_get_recommender_uuid = get_recommender_uuid_patcher.start() + mock_get_recommender_uuid.return_value = "" + from softwarecenter.ui.gtk3.views.catview_gtk import get_test_window_catview # get the widgets we need win = get_test_window_catview() @@ -100,8 +107,15 @@ # patch out the agent query method to avoid making the actual server call @patch('softwarecenter.backend.recagent.RecommenderAgent' - '.query_submit_profile') + '.post_submit_profile') def test_subcatview_recommended_for_you_spinner_display(self, mock_query): + + # patch the recommender UUID value to insure that we are not opted-in for this test + get_recommender_uuid_patcher = patch('softwarecenter.backend.recagent.RecommenderAgent._get_recommender_uuid') + self.addCleanup(get_recommender_uuid_patcher.stop) + mock_get_recommender_uuid = get_recommender_uuid_patcher.start() + mock_get_recommender_uuid.return_value = "" + from softwarecenter.ui.gtk3.views.catview_gtk import get_test_window_catview # get the widgets we need win = get_test_window_catview() @@ -114,13 +128,29 @@ from softwarecenter.ui.gtk3.widgets.containers import FramedHeaderBox self.assertTrue(rec_panel.spinner_notebook.get_current_page() == FramedHeaderBox.SPINNER) self.assertTrue(rec_panel.opt_in_vbox.get_property("visible")) - self.assertTrue(rec_panel.spinner.spinner_label) - win.destroy() - + # now pretent that we got data and ensure its displayed + rec_panel._update_recommended_for_you_content() + rec_panel.recommended_for_you_cat._recommend_me_result( + None, make_recommender_agent_recommend_me_dict()) + self._p() + self.assertTrue(rec_panel.recommended_for_you_content.get_property("visible")) + self.assertFalse(rec_panel.opt_in_vbox.get_property("visible")) + # exit after brief timeout + TIMEOUT=100 + GObject.timeout_add(TIMEOUT, lambda: win.destroy()) + Gtk.main() + # patch out the agent query method to avoid making the actual server call @patch('softwarecenter.backend.recagent.RecommenderAgent' - '.query_submit_profile') + '.post_submit_profile') def test_subcatview_recommended_for_you_display_recommendations(self, mock_query): + + # patch the recommender UUID value to insure that we are not opted-in for this test + get_recommender_uuid_patcher = patch('softwarecenter.backend.recagent.RecommenderAgent._get_recommender_uuid') + self.addCleanup(get_recommender_uuid_patcher.stop) + mock_get_recommender_uuid = get_recommender_uuid_patcher.start() + mock_get_recommender_uuid.return_value = "" + from softwarecenter.ui.gtk3.views.catview_gtk import get_test_window_catview # get the widgets we need win = get_test_window_catview() @@ -158,6 +188,6 @@ if __name__ == "__main__": - import logging - logging.basicConfig(level=logging.DEBUG) + #import logging + #logging.basicConfig(level=logging.DEBUG) unittest.main() diff -Nru software-center-5.1.10/test/gtk3/test_recommendations_widgets.py software-center-5.1.11/test/gtk3/test_recommendations_widgets.py --- software-center-5.1.10/test/gtk3/test_recommendations_widgets.py 1970-01-01 00:00:00.000000000 +0000 +++ software-center-5.1.11/test/gtk3/test_recommendations_widgets.py 2012-03-01 19:08:21.000000000 +0000 @@ -0,0 +1,30 @@ +#!/usr/bin/python + +from gi.repository import Gtk, GObject +import unittest + +from testutils import setup_test_env +setup_test_env() + +from softwarecenter.ui.gtk3.widgets.recommendations import get_test_window + +# window destory timeout +TIMEOUT=100 + +# FIXME: the code from test_catview that tests the lobby widget should +# move here as it should be fine to test it in isolation + +class TestRecommendationsWidgets(unittest.TestCase): + + def test_recommendations_widgets(self): + win = get_test_window() + win.show_all() + GObject.timeout_add(TIMEOUT, lambda: win.destroy()) + Gtk.main() + + + +if __name__ == "__main__": + #import logging + #logging.basicConfig(level=logging.DEBUG) + unittest.main() diff -Nru software-center-5.1.10/test/gtk3/testutils.py software-center-5.1.11/test/gtk3/testutils.py --- software-center-5.1.10/test/gtk3/testutils.py 2012-02-23 15:41:09.000000000 +0000 +++ software-center-5.1.11/test/gtk3/testutils.py 2012-03-01 19:08:21.000000000 +0000 @@ -194,7 +194,7 @@ def make_recommender_agent_recommend_me_dict(): # best to have a list of likely not-installed items app_dict = { - u'recommendations': [ + u'data': [ { u'package_name': u'clementine' }, diff -Nru software-center-5.1.10/test/test_database.py software-center-5.1.11/test/test_database.py --- software-center-5.1.10/test/test_database.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/test/test_database.py 2012-03-01 11:30:44.000000000 +0000 @@ -458,11 +458,24 @@ self.assertTrue(len(enquirer.get_docids()) > 0) # FIXME: test more of the interface +class UtilsTestCase(unittest.TestCase): + def test_utils_get_installed_package_list(self): from softwarecenter.db.utils import get_installed_package_list installed_pkgs = get_installed_package_list() self.assertTrue(len(installed_pkgs) > 0) + def test_utils_get_installed_apps_list(self): + from softwarecenter.db.utils import ( + get_installed_package_list,get_installed_apps_list) + db = get_test_db() + # installed pkgs + installed_pkgs = get_installed_package_list() + # the installed apps + installed_apps = get_installed_apps_list(db) + self.assertTrue(len(installed_apps) > 0) + self.assertTrue(len(installed_pkgs) > len(installed_apps)) + def make_purchased_app_details(db=None, supported_series=None): """Return an AppDetail instance with the required attributes.""" @@ -542,6 +555,20 @@ # ensure that archive.canonical.com archive roots are detected # as the partner channel self.assertEqual(app_details.date_published, "2012-01-21 02:15:10") + + @patch("softwarecenter.db.update.get_region_cached") + def test_region_blacklist(self, get_region_cached_mock): + from softwarecenter.region import REGION_BLACKLIST_TAG + get_region_cached_mock.return_value = { "countrycode" : "es", + } + app_dict = make_software_center_agent_app_dict() + app_dict["debtags"] = ["%s%s" % (REGION_BLACKLIST_TAG, "es"), + ] + # see _get_app_details_from_app_dict + item = PistonResponseObject.from_dict(app_dict) + parser = SCAApplicationParser(item) + doc = make_doc_from_parser(parser, self.db._aptcache) + self.assertEqual(doc, None) class AppDetailsPkgStateTestCase(unittest.TestCase): diff -Nru software-center-5.1.10/test/test_recagent.py software-center-5.1.11/test/test_recagent.py --- software-center-5.1.10/test/test_recagent.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/test/test_recagent.py 2012-03-01 11:30:44.000000000 +0000 @@ -3,13 +3,18 @@ from gi.repository import GObject import unittest import os +import uuid + +from mock import patch from testutils import setup_test_env setup_test_env() from softwarecenter.backend.recagent import RecommenderAgent -from softwarecenter.testutils import make_recommender_profile_upload_data +from softwarecenter.testutils import ( + get_test_db +) class TestRecommenderAgent(unittest.TestCase): """ tests the recommender agent """ @@ -27,7 +32,26 @@ else: os.environ["SOFTWARE_CENTER_RECOMMENDER_HOST"] = self.orig_host - def on_query_done(self, recagent, data): + @patch('softwarecenter.backend.recagent.SpawnHelper' + '.run_generic_piston_helper') + def test_mocked_recagent_post_submit_profile(self, mock_spawn_helper_run): + def _patched_on_submit_profile_data(*args, **kwargs): + piston_submit_profile = {} + recommender_agent.emit("submit-profile-finished", + piston_submit_profile, + uuid.uuid1()) + mock_spawn_helper_run.side_effect = _patched_on_submit_profile_data + recommender_agent = RecommenderAgent() + recommender_agent.connect("submit-profile-finished", self.on_query_done) + recommender_agent.connect("error", self.on_query_error) + db = get_test_db() + recommender_agent.post_submit_profile(db) + self.assertFalse(self.error) + args, kwargs = mock_spawn_helper_run.call_args + self.assertNotEqual(kwargs['data'][0]['uuid'], None) + self.assertNotEqual(kwargs['data'][0]['package_list'], []) + + def on_query_done(self, recagent, data, uuid=""): print "query done, data: '%s'" % data self.loop.quit() @@ -44,17 +68,20 @@ recommender_agent.query_server_status() self.loop.run() self.assertFalse(self.error) - - # FIXME: disabled for now as the server is not quite working - def disabled_test_recagent_query_submit_profile(self): + + # FIXME: disabled for now as the server is not quite working + def disabled_test_recagent_post_submit_profile(self): # NOTE: This requires a working recommender host that is reachable recommender_agent = RecommenderAgent() - recommender_agent.connect("submit-profile", self.on_query_done) + recommender_agent.connect("submit-profile-finished", self.on_query_done) recommender_agent.connect("error", self.on_query_error) - recommender_agent.query_submit_profile(data=make_recommender_profile_upload_data()) + db = get_test_db() + recommender_agent.post_submit_profile(db) self.loop.run() self.assertFalse(self.error) + #print mock_request._post + # NOTE: this server call is currently not needed and not used # def disabled_test_recagent_query_submit_anon_profile(self): # # NOTE: This requires a working recommender host that is reachable # recommender_agent = RecommenderAgent() @@ -67,6 +94,7 @@ # self.loop.run() # self.assertFalse(self.error) + # FIXME: disabled for now as the server is not quite working def disabled_test_recagent_query_profile(self): # NOTE: This requires a working recommender host that is reachable recommender_agent = RecommenderAgent() @@ -116,7 +144,7 @@ def test_recagent_query_error(self): # NOTE: This tests the error condition itself! it simply forces an error # 'cuz there definitely isn't a server here :) - os.environ["SOFTWARE_CENTER_RECOMMENDER_HOST"] = "https://orange.staging.ubuntu.com" + os.environ["SOFTWARE_CENTER_RECOMMENDER_HOST"] = "https://test-no-server-here.staging.ubuntu.com" recommender_agent = RecommenderAgent() recommender_agent.connect("recommend-top", self.on_query_done) recommender_agent.connect("error", self.on_query_error) diff -Nru software-center-5.1.10/test/test_utils.py software-center-5.1.11/test/test_utils.py --- software-center-5.1.10/test/test_utils.py 2012-02-16 15:13:31.000000000 +0000 +++ software-center-5.1.11/test/test_utils.py 2012-03-01 11:32:16.000000000 +0000 @@ -85,7 +85,7 @@ def test_no_display_desktop_file(self): from softwarecenter.utils import is_no_display_desktop_file - d = "/usr/share/app-install/desktop/wine1.3:wine.desktop" + d = "/usr/share/app-install/desktop/wine1.4:wine.desktop" self.assertTrue(is_no_display_desktop_file(d)) d = "/usr/share/app-install/desktop/software-center:ubuntu-software-center.desktop" self.assertFalse(is_no_display_desktop_file(d)) @@ -124,6 +124,42 @@ uuid = get_uuid() self.assertTrue(uuid and len(uuid) > 0) +class TestExpungeCache(unittest.TestCase): + + def test_expunge_cache(self): + import subprocess + import tempfile + dirname = tempfile.mkdtemp('s-c-testsuite') + for name, content in [ ("foo-301", "status: 301"), + ("foo-200", "status: 200"), + ("foo-random", "random"), + ]: + fullpath = os.path.join(dirname, name) + open(fullpath, "w").write(content) + # set to 1970+1s time to ensure the cleaner finds it + os.utime(fullpath, (1,1)) + res = subprocess.call(["../utils/expunge-cache.py", dirname]) + # no arguments + self.assertEqual(res, 1) + # by status + res = subprocess.call(["../utils/expunge-cache.py", + "--debug", + "--by-unsuccessful-http-states", + dirname]) + self.assertFalse(os.path.exists(os.path.join(dirname, "foo-301"))) + self.assertTrue(os.path.exists(os.path.join(dirname, "foo-200"))) + self.assertTrue(os.path.exists(os.path.join(dirname, "foo-random"))) + + # by time + res = subprocess.call(["../utils/expunge-cache.py", + "--debug", + "--by-days", "1", + dirname]) + # now we expect the old file to be gone but the unknown one not to + # be touched + self.assertFalse(os.path.exists(os.path.join(dirname, "foo-200"))) + self.assertTrue(os.path.exists(os.path.join(dirname, "foo-random"))) + if __name__ == "__main__": import logging diff -Nru software-center-5.1.10/test/testutils.py software-center-5.1.11/test/testutils.py --- software-center-5.1.10/test/testutils.py 2012-02-23 15:41:09.000000000 +0000 +++ software-center-5.1.11/test/testutils.py 2012-03-01 19:08:21.000000000 +0000 @@ -194,7 +194,7 @@ def make_recommender_agent_recommend_me_dict(): # best to have a list of likely not-installed items app_dict = { - u'recommendations': [ + u'data': [ { u'package_name': u'clementine' }, diff -Nru software-center-5.1.10/utils/expunge-cache.py software-center-5.1.11/utils/expunge-cache.py --- software-center-5.1.10/utils/expunge-cache.py 1970-01-01 00:00:00.000000000 +0000 +++ software-center-5.1.11/utils/expunge-cache.py 2012-03-01 11:30:44.000000000 +0000 @@ -0,0 +1,80 @@ +#!/usr/bin/python + +""" +Expunge httplib2 caches +""" + +import argparse +import logging +import os +import time +import sys + +class ExpungeCache(object): + def __init__(self, dirs, args): + self.dirs = dirs + # days to keep data in the cache (0 == disabled) + self.keep_time = 60*60*24* args.by_days + self.keep_only_http200 = args.by_unsuccessful_http_states + self.dry_run = args.dry_run + + def _rm(self, f): + if self.dry_run: + print "Would delete: %s" % f + else: + logging.debug("Deleting: %s" % f) + os.unlink(f) + + def clean(self): + # go over the directories + now = time.time() + for d in self.dirs: + for root, dirs, files in os.walk(d): + for f in files: + fullpath = os.path.join(root, f) + header = open(fullpath).readline().strip() + if not header.startswith("status:"): + logging.debug( + "Skipping files with unknown header: '%s'" % f) + continue + if self.keep_only_http200 and header != "status: 200": + self._rm(fullpath) + if self.keep_time: + mtime = os.path.getmtime(fullpath) + logging.debug("mtime of '%s': '%s" % (f, mtime)) + if (mtime + self.keep_time) < now: + self._rm(fullpath) + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description='clean software-center httplib2 cache') + parser.add_argument( + '--debug', action="store_true", + help='show debug output') + parser.add_argument( + '--dry-run', action="store_true", + help='do not act, just show what would be done') + parser.add_argument( + 'directories', metavar='directory', nargs='+', type=str, + help='directories to be checked') + parser.add_argument( + '--by-days', type=int, default=0, + help='expire everything older than N days') + parser.add_argument( + '--by-unsuccessful-http-states', action="store_true", + help='expire any non 200 status responses') + args = parser.parse_args() + + if args.debug: + logging.basicConfig(level=logging.DEBUG) + else: + logging.basicConfig(level=logging.INFO) + + # sanity checking + if args.by_days == 0 and not args.by_unsuccessful_http_states: + print "Need either --by-days or --by-unsuccessful-http-states argument" + sys.exit(1) + + # do it + cleaner = ExpungeCache(args.directories, args) + cleaner.clean()